According to the documentation if you use the default useProtocolCachePolicy
the logic is as follows:
- If a cached response does not exist for the request, the URL loading system fetches the data from the originating source.
- Otherwise, if the cached response does not indicate that it must be revalidated every time, and if the cached response is not stale (past its expiration date), the URL loading system returns the cached response.
- If the cached response is stale or requires revalidation, the URL loading system makes a HEAD request to the originating source to see if the resource has changed. If so, the URL loading system fetches the data from the originating source. Otherwise, it returns the cached response.
However, my experimentation (see below) has shown this to be completely false. Even if the response is cached it is never used, and no HEAD
request is ever made.
Scenario
I am making a request to a URL that returns ETag
and Last-Modified
headers which never change. I have made the request at least once so the response is already cached (which I can verify by looking at the cache DB for the app on the iOS simulator)
Using useProtocolCachePolicy
(the default)
If I have a URLSession
with a URLSessionConfiguration
with requestCachePolicy
set to useProtocolCachePolicy
then the response is cached (I can see it in the cache DB), but the cached response is never used. Repeated requests to the same URL always make a new GET
request without If-None-Match
or If-Modified-Since
headers, so the server always returns HTTP 200 with the full response. The cached response is ignored.
Using reloadRevalidatingCacheData
on every URLRequest
If I set the cachePolicy
on each URLRequest
to reloadRevalidatingCacheData
then I see caching in action. Each time I make the request, a GET
request is made with the If-None-Match
and If-Modified-Since
headers set to the values of the ETag
and Last-Modified
headers, respectively, of the cached response. As nothing has changed, the server responds with a 304 Not Modified
, and the locally cached response is returned to the caller.
Using reloadRevalidatingCacheData
only on the URLSessionConfiguration
If I only set requestCachePolicy = . reloadRevalidatingCacheData
on the URLSessionConfiguration
(instead of on each URLRequest
) then when the app starts only the first request uses cache headers and gets a 304 Not Modified
response. Subsequent requests are normal GET requests without any cache headers.
Conclusion
All the other cache policy settings are basically variants of "only use cached data" or "never use the cache" so are not relevant here.
There is no scenario in which URLSession
makes a HEAD
request as the documentation claims, and no situation in which it just uses cached data without revalidation based on expiration date information in the original response.
The workaround I will use is to set cachePolicy = .reloadRevalidatingCacheData
on every URLRequest
to get some level of local caching, as 304 Not Modified
response only return headers and no data so there is a saving of network traffic.
If anyone as any better solutions, or knows how to get URLSession
working as documented, then I would love to know.