如何使用Swift完成处理程序的结果?

时间:2020-11-06 02:08:00

标签: swift xcode macos completionhandler

我是Swift和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'

我在做什么错了?

2 个答案:

答案 0 :(得分:0)

正如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()
}

答案 1 :(得分:0)

在阅读了这里的一些评论并进行了更多的研究/实验之后,我开始进行这项工作。这是我更改的内容:

在URL扩展名中,我将其保留为几乎与以这种方式更易读的相同。我确实将csv推到了一个参数:

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,并在那里使用了URL扩展名:

@Published

现在,我的// 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

哇,那真是一次学习的经历。感谢所有发表评论并提供建议的人!