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.
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.reloadIgnoringLocalCacheData
case 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.
reloadIgnoringLocalCacheData ignores the local cache and
reloadIgnoringLocalAndRemoteCacheData ignores local and remote cache.
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.
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
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
reloadIgnoringLocalAndRemoteCacheData is the right one you have to choose.