You better increase URLCache size

In this blog post, I’ll share an observation and advice regarding the caching behavior of network responses by Apple’s APIs.

If you are unfamiliar with caching of network responses then I recommend Apple’s article Accessing cached data that introduces how the iOS URL Loading System can automatically cache responses from network resources.

Unfortunately, this helpful feature, provided by the URLCache class, may not work as often as you might think 😔

Apple “hides” its rules for successful caching in its API documentation for URLSession:dataTask:willCacheResponse:completionHandler:

Responses are cached only when all of the following are true:

  • The request is for an HTTP or HTTPS URL (or your own custom networking protocol that supports caching).

  • The request was successful (with a status code in the 200–299 range).

  • The provided response came from the server, rather than out of the cache.

  • The session configuration’s cache policy allows caching.

  • The provided URLRequest object's cache policy (if applicable) allows caching.

  • The cache-related headers in the server’s response (if present) allow caching.

  • The response size is small enough to reasonably fit within the cache. (For example, if you provide a disk cache, the response must be no larger than about 5% of the disk cache size.)

The last bullet point is critical: Caching will not work if the response size exceeds a certain cache size threshold.

The threshold size is hinted at 5% (might be a bit lower or a bit higher) but what is the cache size?

Apple won’t tell us what the default cache size is. That’s probably because iPhones with different hardware might have different cache sizes.

With help of URLCache APIs we can query the default and actual size of in-memory and disk space.

print("Disk Capacity [MB]: \(URLCache.shared.diskCapacity / 1024 / 1024)")
print("Disk Usage [MB]: \(URLCache.shared.currentDiskUsage / 1024 / 1024)")
print("Memory Capacity [MB]: \(URLCache.shared.memoryCapacity / 1024 / 1024)")
print("Memory Usage [MB]: \(URLCache.shared.currentMemoryUsage / 1024 / 1024)")

My test results for iPhone 15 Pro simulator:

Disk Capacity [MB]: 9
Disk Usage [MB]: 0
Memory Capacity [MB]: 0
Memory Usage [MB]: 0

No wonder any assets (JS/CSS/images) equal to or larger than 700 kilobytes won’t be cached in my tests.

Thankfully we can easily change the cache size 😀

// example how to change the cache size to
//    20 megabytes in memory
//   100 megabytes on disk
URLCache.shared = URLCache(
                    memoryCapacity: 20*1024*1024,
                    diskCapacity: 100*1024*1024,
                    directory: nil
                  )

Changing the shard instance might be the easiest way for your app as long as you apply this change right when your app starts.

For more fine-granular handling, you can create a custom URLCache instance and set it in the URLSessionConfiguration.

Key takeaway: you can ensure the caching of larger responses by increasing the cache size.

Did you find this article valuable?

Support Marco Eidinger by becoming a sponsor. Any amount is appreciated!