我是斯威夫特和SwiftUI的新手。
在我的macOS SwiftUI项目中,我试图验证一个URL是否可以访问,这样我就可以有条件地呈现两个视图中的一个。一个视图加载图像URL,另一个视图在无法访问URL时显示错误图像。
这里是我的URL扩展和完成:
import Foundation
extension URL {
func isReachable(completion: @escaping (Bool) -> Void) {
var request = URLRequest(url: self)
request.httpMethod = "HEAD"
request.timeoutInterval = 1.0
URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil {
DispatchQueue.main.async {
completion(false)
}
return
}
if let httpResp: HTTPURLResponse = response as? HTTPURLResponse {
DispatchQueue.main.async {
completion(httpResp.statusCode == 200)
}
return
} else {
DispatchQueue.main.async {
completion(false)
}
return
}
}.resume()
}
}
在其他地方,我试图在模型视图中使用这一点:
var imageURL: URL? {
if let url = self.book.image_url {
return URL(string: url)
} else {
return nil
}
}
var imageURLIsReachable: Bool {
if let url = self.imageURL {
url.isReachable { result in
return result // Error: Cannot convert value of type 'Bool' to closure result type 'Void'
}
} else {
return false
}
}
尽管Xcode显示了此错误:
Cannot convert value of type 'Bool' to closure result type 'Void'
我做错了什么?
发布于 2020-11-06 05:35:40
在阅读了这里的一些评论并做了更多的研究/实验之后,我才开始工作。我改变的是:
在URL扩展中,我将它保留下来,就像我发现它这样更易读一样。我确实将timeoutInterval
推到了一个参数:
// Extensions/URL.swift
import Foundation
extension URL {
func isReachable(timeoutInterval: Double, completion: @escaping (Bool) -> Void) {
var request = URLRequest(url: self)
request.httpMethod = "HEAD"
request.timeoutInterval = timeoutInterval
URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil {
DispatchQueue.main.async {
completion(false)
}
return
}
if let httpResp: HTTPURLResponse = response as? HTTPURLResponse {
DispatchQueue.main.async {
completion(httpResp.statusCode == 200)
}
return
} else {
DispatchQueue.main.async {
completion(false)
}
return
}
}.resume()
}
}
我修改了我的BookViewModel
,使其中两个属性变为@Published
,并在那里使用了URL扩展:
// View Models/BookViewModel.swift
import Foundation
class BookViewModel: ObservableObject {
@Published var book: Book
@Published var imageURLIsReachable: Bool
@Published var imageURL: URL?
init(book: Book) {
self.book = book
self.imageURL = nil
self.imageURLIsReachable = false
if let url = book.image_url {
self.imageURL = URL(string: url)
self.imageURL!.isReachable(timeoutInterval: 1.0) { result in
self.imageURLIsReachable = result
}
}
}
// Rest of properties...
}
现在,我的BookThumbnailView
可以正确地显示条件视图:
// Views/BookThumbnailView.swift
import SwiftUI
import Foundation
import KingfisherSwiftUI
struct BookThumbnailView: View {
@ObservedObject var viewModel: BookViewModel
private var book: Book {
viewModel.book
}
@ViewBuilder
var body: some View {
if let imageURL = self.viewModel.imageURL {
if self.viewModel.imageURLIsReachable {
KFImage(imageURL)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 70)
.cornerRadius(8)
} else {
ErrorBookThumbnailView()
}
} else {
DefaultBookThumbnailView()
}
}
}
哇,这是很好的学习经验。感谢每一个评论和建议,并提供了提示,以寻找!
发布于 2020-11-06 02:19:40
正如Xcode所告诉的那样,这个问题实际上是出现在行return result
中的。在创建函数func isReachable(completion: @escaping (Bool) -> Void)
时,您要告诉Xcode,您将在(Bool) -> Void
类型中输入一些内容,这应该类似于func someFunction(input: Bool) -> Void
。
但是,当您使用闭包输入完成处理程序时,您将输入一个Bool -> Bool
类型的函数。删除return result
行,或更改func isReachable(completion:)
中的完成类型。
编辑:
实际上,我不建议在计算属性中返回异步结果,这会导致其他问题。
我会把它改为:
func isReachable(completion: @esacping (Bool) -> Void) {
...
}
func showResultView() {
guard let url = imageURL else {
// handling if the imageURL is nil
return
}
url.isReachable { result in
// do something with the result
if result {
// show viewController A
} else {
// show viewController B
}
}
}
// call showResultView anywhere you want, lets say you want to show it whenever the viewController appear
override func viewDidAppear() {
...
showResultView()
}
https://stackoverflow.com/questions/64707897
复制相似问题