Ignorance of the URLRequest cache

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.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.

How useProtocolCachePolicy 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.

Links