Hi team,
While digging into caching behavior in Apollo iOS, I noticed something I wanted to clarify. In CacheReadInterceptor, the .returnCacheDataElseFetch currently checks Apollo’s store first:
case .returnCacheDataElseFetch:
self.fetchFromCache(for: request, chain: chain) { cacheFetchResult in
switch cacheFetchResult {
case .failure:
// Cache miss → proceed to network
chain.proceedAsync(…)
case .success(let graphQLResult):
// Cache hit → return from ApolloStore
chain.returnValueAsync(…)
}
}
This seems to bypass URLSession’s built-in caching behavior. Since Apollo iOS doesn’t have TTL or consider Cache-Control: max-age, once data is cached it will never refresh. By contrast, if we simply proceed without checking Apollo’s store first:
case .returnCacheDataElseFetch:
// Let URLSession + URLCache decide freshness
chain.proceedAsync(...)
then URLSession and URLCache correctly handles max-age and decide whether to serve from cache or hit the network.
I verified this by checking URLSessionTaskMetrics, in URLSessionClient’s func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) which shows whether the response came from localCache or networkLoad.
Looking into the code in v2.0, I’m assuming this is the same behaviour:
public enum CachePolicy_v1: Sendable, Hashable {
func toFetchBehavior() -> FetchBehavior {
switch self {
case .returnCacheDataElseFetch:
return FetchBehavior.CacheFirst
...
}
}
extension FetchBehavior {
public static let CacheFirst = FetchBehavior(
cacheRead: .beforeNetworkFetch,
networkFetch: .onCacheMiss
)
}
My question: is this design choice intentional? Should Apollo always prioritise its own store here, or would it make sense to let URLSession handle freshness instead of Apollo’s store?
Thanks in advance for any clarification! ![]()