Repository navigation
Atom already caches responses today, but that behavior is inherited from the selected SessionConfiguration rather than something Atom or the consumer explicitly chooses. .ephemeral gets a private memory cache, .default gets URLCache.shared, and .background gets none. This PR makes that behavior intentional instead of depending on those defaults.
There is also a smaller gap around response metadata. Today resume(expecting:) gives the decoded model while resume() gives the response. Consumers managing their own persistence or freshness may need both from the same request, so this also adds resume(decoding:) to return the model together with its AtomResponse.
Proposed Solution:
Caching now has two explicit parts: the cache itself and whether an individual request is allowed to use it.
ServiceConfiguration accepts a URLCache?, defaulting to nil:
cache: URLCache?The consumer creates and owns that cache, which also means it can clear it when needed, such as during sign out. Having a cache configured does not automatically enable caching for every endpoint.
Each Requestable separately decides how it can use that cache through ResponseCaching:
public enum ResponseCaching: Equatable, Sendable {
case disabled
case revalidatingWithService
case whileCacheFresh
}The default is .disabled. Atom translates each case into the corresponding URLRequest.CachePolicy:
| Case | Policy | Fresh entry | Heuristic entry | Offline with stored entry |
|---|---|---|---|---|
.disabled |
.reloadIgnoringLocalCacheData |
2 hits | 2 hits | fails |
.revalidatingWithService |
.reloadRevalidatingCacheData |
2 hits, [200, 304] |
2 hits, sends If-Modified-Since |
fails |
.whileCacheFresh |
.useProtocolCachePolicy |
1 hit | 1 hit | serves stored response |
Defaulting to .disabled is intentional. Request deduplication can safely default on because it only combines identical requests that are already in flight. Caching is different because it can return older data, so it needs to be explicitly enabled.
Atom also stamps the cache policy on every request, including .disabled, rather than falling back to the session configuration. Setting a policy directly on the request changes how the session-level policy is handled, so doing this consistently keeps the behavior the same regardless of which SessionConfiguration the consumer uses.
AuthorizedRequestable forwards caching explicitly as well. It forwards protocol members manually, so without this the code would still compile but silently fall back to .disabled.
This also adds resume(decoding:), which returns both the decoded model and the AtomResponse it came from:
DecodedResponse<T>The response contains both values:
model
responseBoth async and completion-handler versions are available. resume(expecting:) now uses resume(decoding:) internally so decoder selection, decoding, AtomError mapping, and the existing Data shortcut continue to live in one place.
Additional Info:
.disabledprevents reading from cache, but does not prevent the response from being stored. Because of that, authenticated consumers should use a memory-only cache and clear the cache during sign out. This is documented in the README..reloadRevalidatingCacheDatadoes work. This was verified even though Apple's documentation suggests otherwise, which is what allows.revalidatingWithServiceto work..revalidatingWithServicerequires the network. It does not fall back to the cached response when offline. That is the same failure behavior.disabledalready has.