20

I'm looking for a network caching solution for my iOS application that is persistent across launches. I started to read about NSURLCache, but didn't see any mention regarding persistence. Does anyone know how this behaves when you use NSURLCache then close and open the app? Does it persist?

Brandon
  • 2,886
  • 3
  • 29
  • 44
  • 3
    Yes. https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html – Brian Nickel Aug 26 '13 at 20:07
  • 1
    To be clearer, whenever you use `NSURLConnection`, it caches results in memory and on disk according to the cache policy and response cache header. The on disk cache implies persistence across loads and I can personally verify this behavior. – Brian Nickel Aug 26 '13 at 20:10
  • @Rob if memory serves, `NSURLCache` behaviour changed in iOS 5, that also being when Apple started differentiating between files the OS may delete at any time, files that should be synchronised via iCloud and files for which neither of those things is true. So could your sources possibly be from the pre-5 world? – Tommy Aug 26 '13 at 21:08
  • 1
    @Tommy Doing a little research, I've found that persistent storage caching is finicky, but works in iOS. I've found that it won't cache to persistent storage if (a) you use the default `NSURLRequest` `cachePolicy` of `NSURLRequestUseProtocolCachePolicy`; but (b) the response doesn't include the `Cache-Control` header. But if you use a `cachePolicy` of `NSURLRequestReturnCacheDataElseLoad`, or if the response from the server specifies a particular `Cache-Control` header (e.g. `public, max-age=1835400`), it will cache to persistent storage. Or you can manually add to `NSURLCache`, too. – Rob Aug 27 '13 at 05:23
  • Your wording *could* be confusing. Please correct me if I'm wrong: **Generally:** Caching and persistence are orthogonal. **Caching** is to improve performance and may or may not be persisted depending on needs. **Persistence** is simply “how do I persist any data across an app restart?” Any persistence technique can be used as a backend for persisting “cache" data. Since cache data is just data. The term “cache" just communicates the data’s intended use case. **Specifically:** here you're talking about **persisting the cache** through different launches which I believe *is* correct. – mfaani Jul 14 '17 at 20:30

1 Answers1

14

NSURLCache automatically caches requests for requests made over NSURLConnection and UIWebViews according to the cache response from the server, the cache configuration, and the request's cache policy. These responses are stored in memory and on disk for the lifetime of the cache.


Aside

I validated the behavior with the following code. You do not need to use any of the below in your own code. This is just to demonstrate how I confirmed the behavior.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Prime the cache.
    [NSURLCache sharedURLCache];
    sleep(1); // Again, this is for demonstration purposes only. I wouldn't do this in a real app.

    // Choose a long cached URL.
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://cdn.sstatic.net/stackoverflow/img/favicon.ico"]];

    // Check the cache.
    NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
    NSLog(cachedResponse ? @"Cached response found!" : @"No cached response found.");

    // Load the file.
    [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];

    return YES;
}

The code does the following:

  1. Primes the cache. I noticed a behavior where the cache would not return the cached result until the cache had been initialized and had a chance to scan the disk.
  2. Creates a request for a long-cached file.
  3. Checks if a response exists for the URL and displays the status.
  4. Loads the URL.

On first load, you should see "No cached response found." On subsequent runs, you will see "Cached response found!"

Brian Nickel
  • 26,890
  • 5
  • 80
  • 110
  • Thanks for the sample code. Is the reason you are using cachedResponseForRequest just to prove that the response was cached? Do you have to use cachedResponseForRequest whenever you want to hit the cache? I thought that if you made consecutive calls to sendSynchronousRequest, depending on the cache policy set on the NSURLRequest, that it would automatically hit the cache for you. Is that not the case? – Brandon Aug 26 '13 at 22:24
  • 1
    Sorry, the `cachedResponseForRequest:` call was just to demonstrate that the value was cached. You shouldn't need any special cache handling code to make your cache work. I will update my answer accordingly. – Brian Nickel Aug 26 '13 at 23:04
  • 2
    Why is the sleep(1) line so important? I have a demo downloading app that I've built to feature/prove caching code before I put this sort of code into more complex apps. If I don't add sleep(1) in my app delegate `didFinishLaunchingWithOptions` method after `[NSURLCache setSharedURLCache:sharedCache];` then the whole caching system is broken. It doesn't work. However, if I sleep in my app delegate like described, boom, the entire system works flawlessly. Should I just accept that a weird sleep(1) call is required in my production apps or is there another technique that would do the trick? – John Erck Feb 25 '14 at 01:17
  • 2
    @JohnErck You basically just need some time for the cache to initialize itself before you make your first request, and 1 second was good for me. If you are worried about cache failures for your first requests, you could delay them any number of ways. – Brian Nickel Feb 25 '14 at 21:00
  • 1
    When I close the application and then open it again it loses the entire cache. I would like to open the page that was previously accessed. Is there a way? – André Gava May 31 '19 at 11:19