Cache an image with Swift
Caching images is one of those things that every iOS developer should know how to do, even if it is at the most basic level.
Why and when do we need caching?
Why
It is becoming more and more important to provide a responsive and efficient experience for your users.
Caching is one of the tools that can allow you to do this. Instead of refetching data from a web service, or doing some kind of real time processing that only needs to be done once, caching will let you store or manage those resource in a more performant/efficient way.
When
Example 1:
Let's start with refetching data from a web service. Often times when we consume an api there will be images involved. If you think about an app like Instagram, most of their content would be images.
This is where caching becomes incredibly useful. Being able to store those images on the users device in memory will prevent you from using data to fetch the image every time you need to display it to the user, but it will also improve the user experience because you can load the image from memory.
Example 2:
Another situation where caching is very useful is when you are doing some cpu/gpu intensive processing. You might have a work load that has to do a certain calculation, or apply a filter to an image. Instead of applying the filter every time you need to display the image, you can cache the filtered version of the image so that there is no processing that needs to be done. This can also save a users battery life dramatically depending on the workloads that you need to do.
Use a Dictionary as an image cache
A Dictionary
can be used as a cache, but it is not recommended. Apple has a caching mechanism built in, but before we look at that let's look at how a dictionary can be used to cache images.
Using a dictionary as a cache is incredibly straight forward. I have created a new app and I have updated my ViewController
to look like the following:
class ViewController: UIViewController {
// 1
var dictionaryImageCache: Dictionary<String, UIImage> = [String:UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
// 2
self.dictionaryImageCache["testKey"] = UIImage()
}
}
- Using a
Dictionary
where the key is aString
and the value will be aUIImage
. - Creating a new instance of
UIImage
and assigning it totestKey
on theDictionary
.
That is all it takes to create a very simple cache. With this you would not need to re-download the image because it would be in the Dictionary
and whenever you need to retrieve the image you will use the key that you assigned the image to.
Use NSCache as an image cache
NSCache
is a key-value mutable collection. NSCache has a few special features that help with being a cache. Even though a Dictionary
can be used, it shouldn't be used for cache purposes.
The following features are what makes NSCache
different from using something like a Dictionary
:
NSCache
has built in functionality that will ensure that the cache does not use too much system memory. If the system needs memory,NSCache
will remove some items from cache in order to reduce the amount of memory being used.NSCache
allows you to add, remove and query items in the cache from different threads. This means that you do not need to lock the cache manually.- Last but not least,
NSCache
does not copy the key objects that are put in it, whichNSMutableDictionary
does.
Apple documentation on NSCache
I have updated the code to use NSCache
:
class ViewController: UIViewController {
// 1
var nsCache = NSCache<NSString, UIImage>()
override func viewDidLoad() {
super.viewDidLoad()
// 2
self.nsCache.setObject(UIImage(),
forKey: "testKey")
}
}
Using NSCache
is very similar to using a Dictionary
:
- We create a new instance of
NSCache
.NSCache
requires us to use anNSString
for this example as the key needs to be aclass
not astruct
. - To set an object on
NSCache
we need to use thesetObject
method. This method will take two parameters, each parameter type is based on the types that we initialised theNSCache
with. In our case,UIImage
andNSString
.
In order to access an object stored in the NSCache
you can do the following:
self.nsCache.object(forKey: "testKey")
Conclusion
Caching is an incredibly useful tool that developers have in the toolbox, make sure to use them when possible and when it makes sense.
Also, make sure to use NSCache over a simple dictionary if you need a key-value cache.
The full source code is available here.
If you want to learn more about caching in swift, John Sundell has a good article on it.