NSURLRequest: Always getting NSCachedURLResponse back. - ios

I'm using AfNetworking, I make a call for new data but I keep getting the cached result back. So if I'm in a VC that's shows the data, pop back to the root and change data on my server, I then wait 30sec and when I push back into the VC I'll see the the old data. If I hit the URL in a browser I see the correct data. If I re-run the app I will see the changes to the data.
My response from my server sends back cache control header: Cache-Control:max-age=10, public
From what I can tell is that I always always get a NSCachedURLResponse back and that the cache is not listening to my cache-controll policy.
In my AppDelegate I set my SharedURLCache:
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:8 * 1024 * 1024 diskCapacity:8 * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
How I set the URLRequest:
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
I do nothing else involving NSURLCache.
Any ideas?
BooRanger

The behavior you describe is exactly what I'd expect from the code you've posted. Have you tried using NSURLRequestReloadIgnoringLocalAndRemoteCacheData instead of NSURLRequestUseProtocolCachePolicy? According to this NSURLRequestUseProtocolCachePolicy means:
Specifies that the caching logic defined in the protocol
implementation, if any, is used for a particular URL load request.
This is the default policy for URL load requests.
So if you want it to always load from the server, you should use NSURLRequestReloadIgnoringLocalAndRemoteCacheData which is described as:
Specifies that not only should the local cache data be ignored, but
that proxies and other intermediates should be instructed to disregard
their caches so far as the protocol allows.

Related

iOS: NSURLCache with NSURLSession alongside HTTP headers

NSURLCache is some kind of a dark art magic that is neither documented properly, nor behaves as expected.
I am using AFNetworking's AFHTTPSessionManager to utilise NSURLSession. I am using this mocking service to create custom HTTP responses.
I am setting the NSURLCache with disk capacity and all the cache policies.
Then in the URLSession:task:didCompleteWithError method of NSURLSessionTaskDelegate, I am doing this:
NSURLRequest *request = task.currentRequest;
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
The documentation states about the cachedResponseForRequest:
#result The NSCachedURLResponse stored in the cache with the given
request, or nil if there is no NSCachedURLResponse stored with the
given request.
AFNetworking's FAQ states:
So long as your NSURLRequest objects have the correct cache policy,
and your server response contains a valid Cache-Control header,
responses will be automatically cached for subsequent requests.
After all this my assumption is that the value of the the cachedResponse variable will be nil if the Cache-Control is NOT set properly. However, whatever I have tried to do so far - setting it to 'private', 'no-cache' or whatever - the cachedResponse ALWAYS contains the cached response. How can I verify that the caching mechanism works as expected ? Is there anything that I am missing from the setup ?
I have been doing my tests by firstly making the request online, then switching off the internet on my computer. XCode 7.0 beta iOS 9.0.
Any help is appreciated.

Setting NSURLRequestCachePolicy and NSURLSession

I'm creating an NSURLSession with a sessionConfiguration where I have explicitly set the requestCachePolicy and URLCache.
sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad;
sessionConfiguration.URLCache = [NSURLCache sharedURLCache];
A request is created and passed as an argument in NSURLSession dataTaskWithRequest:CompletionHandler: method. The request is constructed like this:
request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:kAENServiceNetworkTimeout];
Problem 1:
Setting up like this, no fsCachedData Folder is created on disk and cached data is not used. If the request is created simply using:
request = [NSMutableURLRequest requestWithURL:url];
The cache is created on disk, and the image is used as expected. Is this a bug in Apple's code?
Problem 2:
In neither of these cases is the URLSession:dataTask:willCacheResponse:completionHandler: delegate method called. Are their situations under which this would be the case?
Update:
I've pushed an example project to Github, https://github.com/mattmorton/cache-testing
Problem 1:
I think my understanding of the NSURLRequestReturnCacheDataElseLoad was incorrect. In this case the response is not cached.
Problem 2:
From Apple header for NSURLSession in the NSURLSessionAsynchronousConvenience category:
data task convenience methods...create tasks that bypass the normal delegate calls for response and data delivery, and provide a simple cancelable asynchronous interface to receiving data.
I am using a convenience method handler, hence why the delegate method is not being called.

Remove UIWebView's internal cache

I'm showing a web app in an UIWebView, and sometimes the content of pages will change. After content have been changed the app clears the cache. But when I go to a page I've previously visited the UIWebView doesn't send a HTTP GET request, but loads from cache even though I've disabled cache like so:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
Initally I'm loading a request with cachePolicy cachePolicy:NSURLRequestReturnCacheDataElseLoad.
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:myURLString] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:10.0]];
UIWebView have some kind of internal cache. Already visited pages will be loaded from this internal cache instead of going through NSURLCache and also there's no request sent.
Is there any way to clear the internal cache of UIWebView? I'm even recreating the UIWebView but the cache is still there.
It appears that what's happening here is that it reloads the actual HTML file, but does not necessarily reload the resources within that page.
A possible solution I've seen is to append a query parameter on to the end of the URL. For example:
NSString *testURL = [NSString stringWithFormat:#"%#?t=%#", url, randQuery];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:testURL] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10.0]];
where you generate a random alphanumeric string as your randQuery query parameter, or keep a persistent count and just count up.
This should force the UIWebView to load from the remote resource.
I had the same issue and setting HTTPShouldHandleCookies property to NO fixed my problem.
For example:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:strurl]];
[request setHTTPShouldHandleCookies:NO];
[webView loadRequest: request];
Hope this help.

Is NSURLCache persistent across launches?

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?
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:
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.
Creates a request for a long-cached file.
Checks if a response exists for the URL and displays the status.
Loads the URL.
On first load, you should see "No cached response found." On subsequent runs, you will see "Cached response found!"

iOS Retrieve cache in webview

I have a webview to show web content, let said google.com. I use
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30];
[storeWebView loadRequest:request];
on the viewDidLoad function. BUT when I click another button, which is in the web page (not the UIButton), how to I 'save' the cache and when I click another button, or back button, it will find and retrieve the cache?
My understanding is the system will cache the page for you, and if you use same request again, it will first look in the system cache, and if the page is there it will load it up from cache...
to clear cache, use [[NSURLCache sharedURLCache] removeAllCachedResponses];
if you want more control, take a look at NSURLCache Class Reference

Resources