首页 > 解决方案 > 不知道如何得到完成的结果

问题描述

我无法使用完成处理程序的结果。我收到此错误“无法将 '()' 类型的值转换为预期的参数类型”

struct SearchCollectionViewModel {
    let name: String
    let previewURL: String?
    var image:UIImage?
    let dataController = DataController()
}

extension SearchCollectionViewModel {
    init(with result: Result) {
        self.name = result.trackName
        self.previewURL = result.previewURL
        if let url = result.previewURL {
            let imgData = preview(with: url, completion: { data -> Data? in
                guard let data = data as? Data else { return nil }
                return data
            })
            self.image = UIImage(data: imgData)
        }
    }

    private func preview(with url: String, completion: @escaping (Data) -> Data?) {
        dataController.download(with: url) { data, error  in
            if error == nil {
                guard let imageData = data else { return }
                DispatchQueue.main.async {
                    _ = completion(imageData)
                }
            }
        }
    }
}

标签: swiftcompletion

解决方案


几点观察:

  1. 您不能“返回”通过转义闭包异步检索的值。

  2. 闭包定义(Data) -> Data?表明闭包不仅会传递Data为图像检索的值,而且闭包本身也会返回一些东西给preview. 但它显然没有这样做(因此需要_,如_ = completion(...))。我建议您将其更改为(Data?) -> Void(或使用该Result<T, U>模式)。

  3. 我建议重命名您的Result类型,因为有一个众所周知的泛型要求Result<Success, Failure>返回.success(Success)or .failure(Failure)。这是我们已经使用了一段时间的模式,但也正式在 Swift 5 中引入。见SE-0235

    您的代码库可以有自己的Result类型,但如果您开始采用此Result<T, U>约定,它会在以后引起混淆。

  4. 你真的不应该从 启动异步过程init,而是调用一个方法来做到这一点。

  5. 就个人而言,我会将转换移至UIImageDataController例如

    extension DataController {
        func downloadImage(with url: URL, completion: @escaping (UIImage?, Error?) -> Void) {
            let task = URLSession.shared.dataTask(with: url) { data, _, error in
                let image = data.flatMap { UIImage(data: $0) }
                completion(image, error)
            }
            task.resume()
        }
    }
    

所以,我可能会建议你最终得到类似的东西:

class SearchCollectionViewModel {
    let name: String
    let previewURL: String?
    let dataController = DataController()
    var image: UIImage?

    init(with result: Result) {
        self.name = result.trackName
        self.previewURL = result.previewURL
    }
}

extension SearchCollectionViewModel {
    func preview(with url: String, completion: @escaping (UIImage?) -> Void) {
        guard let urlString = previewURL, let url = URL(string: urlString) else {
            completion(nil)
            return
        }

        dataController.downloadImage(with: url) { [weak self] image, error in
            DispatchQueue.main.async {
                self?.image = image
                completion(image)
            }
        }
    }
}

推荐阅读