11

I'm trying to figure out how to use the URL loading framework to load URLs taking advantage of caching.

I am using NSURLConnections and feeding them NSURLRequests. I have even set the cachePolicy on those requests to NSURLRequestReturnCacheDataElseLoad. The first time I load a request, it does automatically get put in the cache ([NSURLCache sharedCache] has it). But the next time I load the same request, the NSURLConnection seems to ignore what's in the cache and reload the data.

Am I supposed to be manually implementing cache lookups and returning cached data? Does NSURLConnection not do this? Or is there some way to get the framework to use the cache seamlessly?

UPDATE: Tried the following without success:

  • Setting the request cache policy to NSURLRequestReturnCacheDataElseLoad instead of NSURLRequestUseProtocolCachePolicy
  • Re-using the request object instead of making a new one
  • Using +[NSURLConnection sendSynchronousRequest:returningResponse:error:] instead of loading asynchronously
jasoncrawford
  • 2,713
  • 2
  • 21
  • 20
  • 2
    Are you feeding them the same NSURLRequest object? The documentation states that the NSURLCache works by mapping a specific NSURLRequest object to the specified response data, so it's possible that even if you are making a request to the same URL, it will still cause a cache miss. – ImHuntingWabbits Dec 10 '09 at 10:23
  • No, I'm making a new NSURLRequest. But when I do `[[NSURLCache sharedURLCache] cachedResponseForRequest:]` with that new request object, there is a cached response. So the NSURLCache somehow knows that the requests are the same. – jasoncrawford Dec 11 '09 at 18:53
  • PS: I tried reusing the NSURLRequest object. Doesn't make a difference. – jasoncrawford Dec 17 '09 at 00:49
  • Where is it requesting the data from? Is it a static page or is it dynamic with appropriate HTTP headers? Perhaps the Expires, If-Modified-Since or Cache-Control headers are overriding the cache? – Ben Sykes Dec 11 '09 at 02:08
  • Good ideas. I've tried both static and dynamic, doesn't seem to make a difference. Don't see any of the headers you metioned on the request or the response. – jasoncrawford Dec 17 '09 at 00:54
  • @Ben Sykes makes a good point about the cache control headers. Likely nothing would be cached unless the appropriate headers are utilized on the response from the server. Does caching work when making requests from a web browser (Chrome / Firefox make it easy to see.) – Norman H Sep 05 '12 at 19:11

3 Answers3

6

NOTE iOS 5 onwards provide a sharedURLCache that has both memory and disk capacity.

Nothing will cache unless you set the NSURLCache to have some capacity:

// A 10MB cache. This a good avatar-image-cache size but might be too 
// large for your app's memory requirements. YMMV.
[[NSURLCache sharedURLCache] setMemoryCapacity:1024*1024*10];

The default iPhone NSURLCache instance refuses to ever cache to disk. if you need this behaviour you must sub-class NSURLCache and implement your own disk cache. I have found numerous examples of disk caches on GitHub, though none of them do the entirely necessary "prune" step satisfactorily IMHO.

mxcl
  • 26,392
  • 12
  • 99
  • 98
  • 1
    It appears that [[NSURLCache sharedURLCache] setMemoryCapacity:1024*1024*10] works but you have to call it every time when making the request because when I called it once in my AppDelegate the cache behavior was totally random between app launches (sometime it worked, sometimes not). – Abduliam Rehmanius Oct 09 '11 at 17:21
  • Even if you do not setup a cache explicitly, NSURLCache still creates a db file and still saves the content! – Abhinit Oct 09 '12 at 10:12
  • 1
    @Abhinit yes, my answer predates iOS 5. Since then the cache is always set up unless you disable it. – mxcl Oct 09 '12 at 14:30
0

It will indeed use NSURLCache automatically, at least in some circumstances. Certainly it does in the following code:

EDIT - works in a OS X 10.6 Cocoa app, not iPhone (misread question)

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {

    // run request with default cache policy
 NSMutableURLRequest *req=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://en.wikipedia.org/"]];
 NSData *data=[NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];
 NSLog(@"Received %d bytes", [data length]);

 sleep(10);

    // now run it asking it to use the cache
 [req setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
 data=[NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];
 NSLog(@"Received %d bytes", [data length]);

    return 0;
}
Nick Moore
  • 15,547
  • 6
  • 61
  • 83
  • Interesting, thanks. For what it's worth, though, I'm doing asynchronous requests--not sure if that matters. – jasoncrawford Dec 11 '09 at 18:54
  • OK, so I actually tried this out, and it's not working for me. The second request still takes several hundred milliseconds, and if I use cache policy `NSURLRequestReturnCacheDataDontLoad`, it returns 0 bytes. Further, if I query the cache directly in between using `[[NSURLCache sharedURLCache] cachedResponseForRequest:req]`, it returns nil. So there's no caching going on here for me. I'm running in the iPhone simulator, simulating OS 3.0 (also tried 3.1.2), under Snow Leopard. – jasoncrawford Dec 17 '09 at 00:48
  • 1
    Right, might be something to do with I am just running directly under OS X. I didn't spot the iphone tag - sorry. – Nick Moore Dec 17 '09 at 02:33
0

Have you tried messing with the connection:willCacheResponse: method? According to the URL Loading System documentation, "By default the data for a connection is cached according to the support provided by the NSURLProtocol subclass that handles the request. An NSURLConnection Delegationdelegate can further refine that behavior by implementing connection:willCacheResponse:."

Colin Barrett
  • 4,451
  • 1
  • 26
  • 33
  • 1
    Yeah, I have tried implementing `connection:willCacheResponse:`--doesn't help. That method does get called, and--here's the kicker--the response definitely ends up in the cache. After the first request, `[[NSURLCache sharedCache] cachedResponseForRequest:request]` returns the cached response. But if I then initialize an NSURLConnection with the same request, it fetches anew rather than hitting the cache. – jasoncrawford Dec 17 '09 at 19:38
  • I know in the past I've actually peeked in the cache and that helped -- although in that particular situation I wanted the method to return immediately rather than load asynchronously if at all possible. – Colin Barrett Dec 18 '09 at 08:29