By default, Nuke uses Foundation.URLCache
which respects HTTP cache control headers. If the headers are not configured correctly, the images are not going to be stored by Foundation.URLCache
.
An example of a properly configured HTTP response:
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
Expires: Mon, 26 Jan 2016 17:45:57 GMT
Last-Modified: Mon, 12 Jan 2016 17:45:57 GMT
ETag: "686897696a7c876b7e"
To learn more about HTTP caching, see Image Caching
Solution
There are multiple approaches how to solve this issue:
- Configure the server to return HTTP cache control headers
- Use custom disk cache which ignores HTTP cache control headers. Nuke already ships with one:
ImagePipeline {
$0.dataCache = try? DataCache(name: "com.myapp.datacache")
}
Using
DataCache
has other advantages like significantly improved performance comapred toFoundation.URLCache
.
By default, Nuke uses Foundation.URLCache
which respects HTTP cache control headers. Cache control typically sets an expiration age for each resource. If the resource expires, URLSession
isn't going to serve it until it goes to the server and validates whether the contents stored in cache are still fresh.
Solution
- Increase the expiration age in HTTP cache control headers
- Use custom disk cache which ignores HTTP cache control headers. Nuke already ships with one:
ImagePipeline {
$0.dataCache = try? DataCache(name: "com.myapp.datacache")
}
Using
DataCache
has other advantages like significantly improved performance comapred toFoundation.URLCache
.
- Force the
URLSession
to return an expired image, and then validate it later in the background. This can easily be done with ImagePublisher or RxNuke.
let cacheRequest = URLRequest(url: url, cachePolicy: .returnCacheDataDontLoad)
let networkRequest = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy)
let cachedImage = pipeline.imagePublisher(with: ImageRequest(urlRequest: cacheRequest)).orEmpty
let networkImage = pipeline.imagePublisher(with: ImageRequest(urlRequest: networkRequest)).orEmpty
cancellable = cachedImage.append(networkImage)
.sink(receiveCompletion: { _ in /* Ignore errors */ },
receiveValue: { imageView.image = $0.image })
- Dynamically switch between
.useProtocolCachePolicy
to.returnCacheDataDontLoad
when network appears to be offline.