Skip to content
This repository

AFURLConnectionOperation behaves differently under iOS5.1 and iOS6.0 #566

Closed
abhinittiwari opened this Issue · 32 comments

9 participants

Abhinit Tiwari Mattt Thompson guykogus Matt "Trip" Maker Steven Fisher Magnus Ottosson alvinhu Geph0rce Hermes Pique
Abhinit Tiwari

To reproduce this issue

  • Make a request, any request and make sure its successful
  • Turn off the internet connectivity
  • Make the same request again

2012-10-09 15:30:43.667 CACHE TEST[3811:1bb03] <NSMutableURLRequest http://myserver.in/someImage.png>
2012-10-09 15:30:43.674 CACHE TEST[3811:1bb03] -[AFURLConnectionOperation connection:didReceiveData:]
2012-10-09 15:30:43.675 CACHE TEST[3811:1bb03] -[AFURLConnectionOperation connectionDidFinishLoading:]

On iOS5, if the app is offline, data is picked up from NSURLCache and connectionDidFinishLoading gets called.


2012-10-09 15:32:34.578 CACHE TEST [3877:1e903] <NSMutableURLRequest http://myserver.in/someImage.png>
2012-10-09 15:32:34.578 CACHE TEST [3877:1e903] -[AFURLConnectionOperation connection:didFailWithError:]

On iOS6, if the app is offline, data is NOT picked up from NSURLCache and connection:didFailWithError gets called.

There has been no change in the way NSURLConnection works. I verified that by writing a quick sample. It does happen with AFNetworking.

Digging deeper. Anyone got any clues?

Mattt Thompson
Owner

Very interesting. Thanks so much for taking the time to make such a thorough issue—I genuinely appreciate that.

Any change in behavior between iOS versions is technically in the domain of NSURLConnection, et al. rather than being an issue with AFNetworking specifically. As such, I would encourage you to file a Radar bug to Apple to hopefully get this fixed in a subsequent release.

The best I can do is document defensively, to try to at least minimize any surprise this change might cause.

Out of curiosity: In your example, is the shared NSURLCache set with just in-memory, or with a both in-memory and disk cache? What values are you specifying for each?

Abhinit Tiwari

I started out believing that this is an issue in the domain of NS*** classes, but if I use AFNetworking, even if I do not set any cachePolicy, I see the mentioned discrepancy.

That is if I just use [NSURLRequest requestWithURL:]

If I use the same with a simple NSURLConnection I do not.

I will document the test with all the possible cachePolicies and get back.

Mattt Thompson
Owner

I have a theory. Could you try testing AFN on iOS 6 by commenting out the implementation of -connection:willCacheResponse:. It could be that the expectations of what a "default" implementation of that has changed between versions.

guykogus

I've just started tackling a bug with a similar issue. I'm trying to load an image with the cache policy NSURLRequestReturnCacheDataElseLoad. When offline on iOS 5 it loads the cache data properly. On iOS 6 I get the following error:
Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline."

This was happening from our custom implementation of the UIImageView+AFNetworking category. @abhinittiwari, since you've been saying that this isn't an NSURLConnection issue I tried the exact same request with the following code:

NSURLResponse *response = nil;
NSError *error = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (error)
{
NSLog(@"%@", error);
}
else
{
NSLog(@"%@", response);
}

And, lo and behold, I'm getting the same error. So as far as I can tell, this is an NSURLConnection issue.

An interesting result: this error only appears to occur on devices that were never using a beta version of iOS 6. I've tested on 2 iPhones and 2 iPads, 1 of each had been on a beta but now all with the proper, final release of iOS 6. The ones that had been on beta perform as usual and return the cached data, the normal ones don't.

A solution to the problem is, instead of loading the request normally, to call [[NSURLCache sharedURLCache] cachedResponseForRequest:request] when you're offline.

Abhinit Tiwari

Ok. I commented out -connection:willCacheResponse: and it remained the same

iOS6

-connection:didFailWithError:

iOS5.1

-connectionDidFinishLoading

@guykogus the device that I am seeing this issue on had beta installed on it earlier. I think we can rule that out. But intrigued to see it working on any device at all.

PS: I am on the latest version.

guykogus

@mattt I commented out -connection:willCacheResponse: and it didn't fix the problem. Mind you I'm on an old version of AFNetworking (last updated July 23).

Abhinit Tiwari

I tested all the possible cachePolicies with iOS5.1 and iOS6.1 and did not record any differences in the way NSURLConnection works.

guykogus

In AFURLConnectionOperation, I put a breakpoint on -connection:willCacheResponse: and tried running my code on both iOS 5.1 and iOS 6.0. On 5.1 I hit the breakpoint on each image load request but on 6.0 this doesn't happen - at least not on the image requests. There is absolutely a change in the behaviour of NSURLConnection on iOS 5 and iOS 6.

I'll keep looking into this and let you know if I can find a proper solution.

Abhinit Tiwari

I have uploaded my test at https://github.com/abhinittiwari/NSURLCacheTest

I have posted the results. I found no difference whatsoever in how the default network classes behave on the 2 OSs

Mattt Thompson
Owner

Perhaps the cache policy behavior is a red herring. At the very least to rule it out, could you check to see if there's a difference between the headers sent in either case? It could be that a cache header is missing in iOS.

guykogus
Abhinit Tiwari

@mattt I confirmed that the request headers in both cases were

{
    Accept = "*/*";
    "Accept-Encoding" = "gzip, deflate";
    "Accept-Language" = "en-us";
}

Do you think testing it on the server side would show something else?

guykogus

I suggest reading this article. There's an important point where he talks about the header missing a Cache-Control field. I don't know if it's exactly your problem but it could be related. Hope it helps.

Mattt Thompson
Owner

Awesome work testing the caching behaviors, @guykogus. There would indeed seem to be significant differences in how NSURLConnection works between 5 and 6. As AFNetworking is merely a layer on top of NSURLConnection, there is unfortunately little that can be done to mitigate these kinds of situations. If anyone can reproduce a failing case, and come up with a reasonable workaround in AFN, I'm all ears.

Mattt Thompson mattt closed this
guykogus

As a small update (though unrelated to this issue), I've filed a bug report with Apple (#12469707) with a test project to indicate the change in caching. I spoke to an Apple rep who said that this is definitely an issue that they need to look into.

Basically, if you load with NSURLConnection using the NSURLRequestReturnCacheDataElseLoad cache policy and cached data exists, you should get back the cached data irrelevant of the cache expiration time. In iOS 5 this works, in iOS 6 the data is reloaded. Even worse, if you're offline you'll still get the cached data in iOS 5, but in iOS 6 you get the error message I mentioned earlier in this thread:
Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline."

FYI :)

Steven Fisher

Thanks for the update on this, @guykogus. This is likely to affect me.

Mattt Thompson
Owner

Yes, thanks for the update @guykogus.

Magnus Ottosson

I have this same issue aswell. Do you have an example of how to fix this for now?

guykogus

In the failure block for a request that I want to use the cached response (if available) I use:

NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
if (cachedResponse != nil &&
    [[cachedResponse data] length] > 0)
{
    // Get cached data
    ....
}
Magnus Ottosson

I guess that would work but I would like to see a solution that does not break the interface of AFNetworking. If I do like you suggested I would have to handle json parsing etc on my own. If (and it sure seems to be) a bug in ios this should be handled in AFNetworking so that the users of AFNetworking does won need to handle this?

Is this a bad idea?

Magnus Ottosson

Is there a way to manually trigger a cached response?

guykogus

Use the code I wrote above. You just create the NSURLRequest object and pass it in to the shared NSURLCache.

Magnus Ottosson

Yes, I know how to get the cached data but this is the pure response from the server right? If I subclass the AFHTTPClient and make a json request the response is parsed as json via the AFJSONRequestOperation right? This is not the same data that exist in the cache. So if I manually fetch the data from the cache like you say I also need to handle the parsing of the data myself?

I could ofcourse implement the suggested solution in the completionBlock implementations of the different operationclasses and that would work I guess.

alvinhu

I got this issue as well. I tried to find code bug of my work for almost 2 days. Finally, I found this page. Thanks for guys that explain it out. I can get rest and try to make a solution to this issue.

Geph0rce

@guykogus

NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
if (cachedResponse != nil &&
[[cachedResponse data] length] > 0)
{
// Get cached data
....
}
can not work with UIWebView, so we have to wait apple to resolve this issue.

Hermes Pique

@guykogus It appears this was fixed in iOS 7. Is there any official confirmation from Apple that this was dealt with?

guykogus

That's not the kind of thing Apple would ever confirm. And anyway, my bug report was marked as duplicate so I wouldn't know if it was officially resolved...

Hermes Pique

@guykogus Such is the usefulness of a closed bug tracking system. Thanks for replying.

guykogus

Just tested the demo project from my old bug report on iOS 7.1 and, yes, the issue has been resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.