Skip to content
This repository has been archived by the owner on May 3, 2021. It is now read-only.

Caching across app launches #8

Closed
bonebox opened this issue Mar 11, 2015 · 8 comments
Closed

Caching across app launches #8

bonebox opened this issue Mar 11, 2015 · 8 comments
Labels

Comments

@bonebox
Copy link

bonebox commented Mar 11, 2015

I'm using a DFImageView in a UICollectionViewCell and calling the setImageWithResource: method. The caching seems to work as long as my app is open, but every time I restart the app it downloads the images again remotely. Is there some additional configuration needed to make the cache persist across app launches?

@kean
Copy link
Owner

kean commented Mar 11, 2015

There is a separate memory cache (NSCache) that stores decompressed images while your app is running. And there is a persistent NSURLCache, which is controlled entirely on the system framework level by NSURLSession, DFImageManager doesn't override any of the system behaviors regarding disk caching.

From the official documentation:

As a rule, 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 NSURLRequest 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.)

Make sure the HTTP caching is implemented properly.

@kean kean added the question label Mar 11, 2015
@mythodeia
Copy link

disk caching would be a great addition to this excellent library 👍

@bonebox
Copy link
Author

bonebox commented Mar 14, 2015

After reading @kean's reply above and digging into the code, DFImageManager does support disk caching, through NSURLSession/NSURLCache. The issue is that DFImageManager calls NSURLSessionConfiguration.defaultSessionConfiguration() which by default has disk cache disabled. After doing some further research, this can be solved 1 of 2 different ways:

  1. If you have access to server-side config, you can supply the Cache-Control header in the image response, and your app will cache without any additional config. (http://nshipster.com/nsurlcache/)
  2. You can enable disk caching through DFImageManager with a custom DFImageManagerConfiguration. Here's one possible implementation for the sharedManager:
        let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
        configuration.requestCachePolicy = .ReturnCacheDataElseLoad
        // 0 memoryCapacity since in-memory cache is handled by DFImageManager
        configuration.URLCache = NSURLCache(memoryCapacity: 0, diskCapacity: 1024 * 1024 * 256, diskPath: "com.github.kean.default_image_cache")
        configuration.timeoutIntervalForRequest = 60;
        configuration.timeoutIntervalForResource = 60;

        let imageFetcher = DFURLImageFetcher(sessionConfiguration: configuration)
        let imageConfig = DFImageManagerConfiguration(fetcher: imageFetcher, processor: DFImageProcessor(), cache: DFImageCache())
        let imageManager = DFImageManager(configuration: imageConfig)

        DFImageManager.addSharedManager(imageManager)

The key is the NSURLSessionConfiguration requestCachePolicy property.

Not too incredibly painful, but since the primary purpose of this app is image caching, it would be nice if there were a simpler way to set that flag perhaps through a property on DFImageManager.

@mythodeia
Copy link

thanks @bonebox

@kean
Copy link
Owner

kean commented Mar 16, 2015

Thank you @bonebox. I've added this question to the FAQ

These are indeed two intended ways to control image caching:

  1. This is a preferable way. HTTP caching is a universal standard that should be adapted in any possible case.
  2. That's a great choice if you don't have control over the server with misconfigured HTTP cache.

You can also control request cache policy per request (DFImageRequest) by using DFURLRequestCachePolicyKey (see docs for more info)

The issue is that DFImageManager calls NSURLSessionConfiguration.defaultSessionConfiguration() which by default has disk cache disabled.

This is not entirely true, the default NSURLSessionConfiguration.defaultSessionConfiguration() is initialized with NSURLRequestUseProtocolCachePolicy, which enables HTTP cache policy.

Not too incredibly painful, but since the primary purpose of this app is image caching, it would be nice if there were a simpler way to set that flag perhaps through a property on DFImageManager.

Yep, maybe in the future, just not a single flag but an easier way to provide your own NSURLSessionConfiguration.

@bonebox
Copy link
Author

bonebox commented Mar 16, 2015

This is not entirely true, the default NSURLSessionConfiguration.defaultSessionConfiguration() is initialized with NSURLRequestUseProtocolCachePolicy, which enables HTTP cache policy.

From Apple's official documentation:

Cache Use Semantics for the HTTP Protocol

The most complicated cache use situation is when a request uses the HTTP protocol and has set the cache policy to NSURLRequestUseProtocolCachePolicy.

If an NSCachedURLResponse does not exist for the request, then the URL loading system fetches the data from the originating source.

If a cached response exists for the request, the URL loading system checks the response to determine if it specifies that the contents must be revalidated.

If the contents must be revalidated, the URL loading system makes a HEAD request to the originating source to see if the resource has changed. If it has not changed, then the URL loading system returns the cached response. If it has changed, the URL loading system fetches the data from the originating source.

If the cached response doesn’t specify that the contents must be revalidated, the URL loading system examines the maximum age or expiration specified in the cached response. If the cached response is recent enough, then the URL loading system returns the cached response. If the response is stale, the URL loading system makes a HEAD request to the originating source to determine whether the resource has changed. If so, the URL loading system fetches the resource from the originating source. Otherwise, it returned the cached response.

RFC 2616, Section 13 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13) specifies the semantics involved in detail.

This line in particular:

If an NSCachedURLResponse does not exist for the request, then the URL loading system fetches the data from the originating source.

I didn't see that DFImageManager is using NSCachedURLResponse anywhere. Even if it is, from the rest of the documentation it appears that the image headers need to be set appropriately in order for NSURLRequestUseProtocolCachePolicy to cache to disk by default.

@kean
Copy link
Owner

kean commented Mar 16, 2015

I didn't see that DFImageManager is using NSCachedURLResponse anywhere.

It is used by NSURLCache. Caching is completely transparent in NSURLSession, that's why it's not visible in DFImageManager.

Even if it is, from the rest of the documentation it appears that the image headers need to be set appropriately in order for NSURLRequestUseProtocolCachePolicy to cache to disk by default.

Yep, most servers already do that anyway. It's extremely important because browsers rely on HTTP cache.

Our team uses NSURLRequestUseProtocolCachePolicy with multiple HTTP cache strategies. We use last-modified for revalidating user avatars (URLs don't change, content does). And we use simple max-age for URLs which content doesn't change. Works like a charm.

@kean kean closed this as completed May 16, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants
@bonebox @kean @mythodeia and others