Reputation: 1370
I am trying to write public function that loads image async and returns UIImage. I want to use it in filling UITableView with images.
class CommonFunctions {
func loadImageAsync(StringURL: NSString) -> UIImage{
var returningImage = UIImage()
let url = NSURL(string: StringURL)
let requestedURL = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(requestedURL, queue: NSOperationQueue.mainQueue(), completionHandler: {
response, data, error in
if error != nil {
println("there is some error loading image")
}
else {
if let image = UIImage(data: data){
returningImage = image
}
}
})
return returningImage
}
}
the problem is that when I want to use this function :
cell.imageModelImage.image = CommonFunctions.loadImageAsync(CommonFunctions)
Instead of String argument I get the class? Why is that so?
Upvotes: 1
Views: 2179
Reputation: 437552
You ask:
Instead of String argument I get the class? Why is that so?
It is because you are calling it as if it was a class function, but didn't define it as such. If you add the class
keyword on the declaration of the function, you won't see that behavior.
But there are deeper issues here: You cannot return a value from an asynchronous function (because the function will return immediately while the asynchronous request will not complete until later).
One solution is to provide a completion handler that will be called if the image is retrieved successfully:
class func loadImageAsync(stringURL: String, completion: @escaping (UIImage?, Error?) -> Void) {
let url = URL(string: stringURL)!
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
DispatchQueue.main.async { completion(nil, error) }
return
}
let image = UIImage(data: data)
DispatchQueue.main.async { completion(image, nil) }
}.resume()
}
Note, I'm dispatching the completion handler back to the main queue, where all UI updates should take place.
Then, in tableView(_:cellForRowAt:)
, you can supply a block that will be called when the asynchronous request is done:
cell.imageModelImage.image = ... // remember to initialize image view before starting async method
CommonFunctions.loadImageAsync(with: "http://example.com/test.jpg") { image, error in
if let cell = tableView.cellForRow(at: indexPath) as? CustomCell { // make sure cell hasn't scrolled out of view
cell.imageModelImage.image = image
}
}
Note, the above assumes that it's impossible to insert a row in the table in the intervening period of time. If that assumption is not valid, rather than using the old indexPath
, you might have requery your model to identify what IndexPath
is valid for this cell when the asynchronous completion handler is called.
For Swift 2 rendition, see the previous revision of this answer.
Upvotes: 2