I want to create a method to cache an image from an URL, I got the code in Swift since I had used it before, how can I do something similar to this in Objective-C:
import UIKit
let imageCache: NSCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageUsingCacheWithUrlString(urlString: String) {
self.image = nil
if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = cachedImage
return
}
let url = URL(string: urlString)
if let data = try? Data(contentsOf: url!) {
DispatchQueue.main.async(execute: {
if let downloadedImage = UIImage(data: data) {
imageCache.setObject(downloadedImage, forKey: urlString as AnyObject)
self.image = downloadedImage
}
})
}
}
}
Before you convert this, you might consider refactoring to make it asynchronous:
One should never use
Data(contentsOf:)for network requests because (a) it is synchronous and blocks the caller (which is a horrible UX, but also, in degenerate cases, can cause the watchdog process to kill your app); (b) if there is a problem, there’s no diagnostic information; and (c) it is not cancelable.Rather than updating
imageproperty when done, you should consider completion handler pattern, so caller knows when the request is done and the image is processed. This pattern avoids race conditions and lets you have concurrent image requests.When you use this asynchronous pattern, the
URLSessionruns its completion handlers on background queue. You should keep the processing of the image and updating of the cache on this background queue. Only the completion handler should be dispatched back to the main queue.I infer from your answer, that your intent was to use this code in a
UIImageViewextension. You really should put this code in a separate object (I created aImageManagersingleton) so that this cache is not only available to image views, but rather anywhere where you might need images. You might, for example, do some prefetching of images outside of theUIImageView. If this code is buried in theThus, perhaps something like:
And you'd call it like:
If you wanted to use this in a
UIImageViewextension, for example, you could save theURLSessionTask, so that you could cancel it if you requested another image before the prior one finished. (This is a very common scenario if using this in table views and the user scrolls very quickly, for example. You do not want to get backlogged in a ton of network requests.) We couldThere are tons of other things you might do in this
UIImageViewextension (e.g. placeholder images or the like), but by separating theUIImageViewextension from the network layer, one keeps these different tasks in their own respective classes (in the spirit of the single responsibility principle).OK, with that behind us, let us look at the Objective-C rendition. For example, you might create an
ImageManagersingleton:and then implement this singleton:
And you'd call it like:
And, again, you could use this from within a
UIImageViewextension: