I am having trouble to use the result of a completion handler. I am getting this error "Cannot convert value of type '()' to expected argument type"
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)
}
}
}
}
}
A couple of observations:
You cannot “return” a value that is retrieved asynchronously via escaping closure.
The closure definition
(Data) -> Data?
says that the closure not only will be passed theData
retrieved for the image, but that the closure will, itself, return something back topreview
. But it’s obviously not doing that (hence the need for_
, as in_ = completion(...)
). I’d suggest you change that to(Data?) -> Void
(or use theResult<T, U>
pattern).I’d suggest renaming your
Result
type as there’s a well-known generic calledResult<Success, Failure>
for returning.success(Success)
or.failure(Failure)
. This is a pattern that we’ve used for a while, but is formally introduced in Swift 5, too. See SE-0235.Your codebase can have its own
Result
type, but it’s an invitation for confusion later down the road if and when you start adopting thisResult<T, U>
convention.You really shouldn’t be initiating asynchronous process from
init
, but instead invoke a method to do that.Personally, I’d move the conversion to
UIImage
into theDataController
, e.g.So, I might suggest you end up with something like: