Caching and invalidating cache is one of the hardest things in computer science according to Martin Fowler.
Recently I was dealing with cache and invalidating it in three of my applications. I had some serious issues with that and wanted to dig deeper. This time we will discuss URLRequest
caching strategies and how to use it in your apps. I will share some of my learnings and problems that I found.
Creating URLRequest with cache
Not so many of us use caching strategy when creating URLRequest
and hitting the network. If the server you’re accessing doesn’t have caching strategy implemented then making network requests can cause data corruption in your apps.
I had these issues with just a simple request and getting new JSON
data from the network. For some reason, URLRequest
thought that nothing has changed and returned data from internal app cache rather than the network.
The reason for this is that NSURLCache
is set for the application by default according to the documentation. The cache will be purged when the device runs low on disk space, but mostly this isn’t the case. You can control NSURLCache
behavior when launching the app but let’s leave that for another post.
Caching strategies
NSURLRequest
has a property cachePolicy
which sets the caching strategy for the request you’re creating. It is an enum and has several choices defined as constants:
case useProtocolCachePolicy
> Use the caching logic defined in the protocol implementation, if any, for a particular URL load request.case reloadIgnoringLocalCacheData
> The URL load should be loaded only from the originating source.case reloadIgnoringLocalAndRemoteCacheData
> Ignore local cache data, and instruct proxies and other intermediates to disregard their caches so far as the protocol allows.static var reloadIgnoringCacheData: NSURLRequest.CachePolicy
> Replaced by NSURLRequest.CachePolicy.reloadIgnoringLocalCacheDatacase returnCacheDataElseLoad
> Use existing cache data, regardless of age or expiration date, loading from originating source only if there is no cached data.case returnCacheDataDontLoad
> Use existing cache data, regardless of age or expiration date, and fail if no cached data is available.case reloadRevalidatingCacheData
> Use cache data if the origin source can validate it; otherwise, load from the origin
If you just read the documentation then all of these constants look confusing and hard to choose the right now. Let’s try to understand which of the caching policy you need to choose for your URLRequest
and when.
Which one to choose?
The default policy for URL load requests is useProtocolCachePolicy
. If a cached response does not exist then it is fetched from the originating source. Otherwise, if a response doesn’t tell to revalidate then a response is returned from the cache. For more detailed information you can go to RFC 2616 detailed documentation. Here is an image to illustrate how this policy works.
Option reloadIgnoringLocalCacheData
ignores the local cache and reloadIgnoringLocalAndRemoteCacheData
ignores local and remote cache.
With returnCacheDataElseLoad
you tell to use cache no matter how out of date it is. If the cached request doesn’t exist it will be loaded from the network.
Option returnCacheDataDontLoad
is the most confusion one. It means offline mode. Only cached data will be used and it won’t load from the network.
But the story doesn’t end here, if we check reloadRevalidatingCacheData
documentation then we see that previous versions than macOS 15, iOS 13, watchOS 6, and tvOS 13 don’t implement this constant. Mattt was warning us about that years ago. There is a radar opened in May 2012.
So which one to choose? There isn’t a right or wrong answer, but the rule of thumb is - if you want partial cache with default settings then choose useProtocolCachePolicy
. If you want to load a request without cache then choose either reloadIgnoringLocalCacheData
or reloadIgnoringLocalAndRemoteCacheData
.
TL;DR
Handling cache and cache invalidation are one of the hardest topics in Computer Science. In iOS, macOS, tvOS and watchOS it isn’t easy and straight forward. The official documentation is confusing and isn’t clear how it works behind the scenes.
URLRequest
has a property cachePolicy
which sets the caching strategy for the request.
For most of the cases default useProtocolCachePolicy
option is what you want. If you want to avoid cache then one of reloadIgnoringLocalCacheData
or reloadIgnoringLocalAndRemoteCacheData
is the right one you have to choose.