Deep dive into Apple’s URLRequest Cache Policies
Hands-On analysis of Apple’s Cache Policies working together with a Node.js Express Server to increase API efficiency and performance
There are only two hard things in Computer Science: cache invalidation and naming things.
— Phil Karlton
Fortunately for us, Apple helps us by providing robust and powerful cache policies. Policies we should use instead of building a custom (NSCache) solution!
In this post, we’ll explore different URLRequest cache policies and the consequences of using them. Those policies apply to both iOS and macOS. We’ll use a hands-on approach looking both on server and front-end sides.
To make things easy, the Node.js Express Server (the Backend) will return strings, and we’ll log the responses in the console. (Check out the source code for both Server and App at the bottom).
Cache Policy #1: useProtocolCachePolicy
The official Apple’s documentation shows a well-presented decision tree. https://developer.apple.com/documentation/foundation/nsurlrequest/cachepolicy/useprotocolcachepolicy.
When looking at the responses, we are looking for both Cache-Control and the HTTP Etag. There are a couple of scenarios we will look at:
Cache-Controlis not set, and
The app did connect to the server, but URLSession ignored the updated response. It might not be what we expect as we can use Etag on the server to sync the app with the server after updating the assets on the server.
Cache-Control is not set, and
Etag is changed every 2 seconds:
Now every time the
Etag changes, the URLSession gives us an updated asset.
Cache-Control is set to 4 seconds, and
Etag is changed every time the server asset changes.
After receiving the first response with the
max-age header set to 4 seconds, the app was hitting the server every 3 seconds. Still, the server returned the URLSession response every second with the status code 200. Secondly, the asset was updated when fetched from the server.
Cache-Control is set to 0 seconds, and
Etagis not set.
We hit the server on every call, but URLSession returns the same asset each time.
Cache-Controlis set to 0 seconds (or not set at all), and
Etag changes on each incoming request
We hit the server on every call, and URLSession gives us the most recent asset version. This is the same behavior we would get from using the CachePolicy
reloadIgnoringLocalCacheData , always returning the latest asset version.
Cache Policy #2: returnCacheDataElseLoad
Apple’s documentation says: “Use existing cache data, regardless of age or expiration date, loading from originating source only if there is no cached data.”
When we use this policy, no matter how we set (or not set) both
Cache-Control on the server, we’ll get just one “snapshot” of the asset, and the server will never be hit again unless we clear the local cache with for example:
It seems like
URLRequest.CachePolicy.returnCacheDataElseLoad is quite a dangerous policy!
Cache Policy #3: reloadIgnoringLocalCacheData
For this cache policy, Apple’s documentation says: “This policy specifies that no existing cache data should be used to satisfy a URL load request.”
No matter how we set
Cache-Policy header or
Etag, the server will be hit every second, and the latest asset will be returned. This policy should be handled with care to avoid overloading the server!
Cache Policy #4: returnCacheDataDontLoad
Apple describes the cache policy as: “If there is no existing data in the cache corresponding to a URL load request, no attempt is made to load the data from the originating source, and the load is considered to have failed. This constant specifies a behavior that is similar to an “offline” mode.”
And indeed, the results seen above are very similar to an offline mode. As you can see, our Express server was not hit by a request once!
Let’s hit our server using different cache policies at once.
Each policy needs to be directed to a unique endpoint. If the endpoints are the same, the cache will be overwritten every time we run a request with the policy
This is what happens if we hit the same endpoint using different caching policies:
The URLRequest response is overwritten, and the server is only hit once by
In my opinion, we should use Apple’s caching capabilities, but some of the behaviors we analyzed above might not be so obvious after reading the documentation.
Below is the Swift code (OSX Command Line App):
And here’s our Nodejs server code:
Next Level Swift
Next Level Swift aims at sharing knowledge and insights into better programming for iOS and is dedicated to help…
We are always looking for talented and passionate Swift developers! Feel free to check out our writer's section and find out how you can share your knowledge with the Next Level Swift Community!