New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
abhinittiwari opened this Issue Oct 9, 2012 · 36 comments

Comments

Projects
None yet
10 participants
@abhinittiwari

abhinittiwari commented Oct 9, 2012

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

This comment has been minimized.

Show comment
Hide comment
@mattt

mattt Oct 9, 2012

Contributor

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?

Contributor

mattt commented Oct 9, 2012

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?

@abhinittiwari

This comment has been minimized.

Show comment
Hide comment
@abhinittiwari

abhinittiwari Oct 9, 2012

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.

abhinittiwari commented Oct 9, 2012

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

This comment has been minimized.

Show comment
Hide comment
@mattt

mattt Oct 9, 2012

Contributor

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.

Contributor

mattt commented Oct 9, 2012

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

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Oct 9, 2012

Contributor

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.

Contributor

guykogus commented Oct 9, 2012

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.

@abhinittiwari

This comment has been minimized.

Show comment
Hide comment
@abhinittiwari

abhinittiwari Oct 9, 2012

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.

abhinittiwari commented Oct 9, 2012

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

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Oct 9, 2012

Contributor

@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).

Contributor

guykogus commented Oct 9, 2012

@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).

@abhinittiwari

This comment has been minimized.

Show comment
Hide comment
@abhinittiwari

abhinittiwari Oct 9, 2012

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

abhinittiwari commented Oct 9, 2012

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

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Oct 9, 2012

Contributor

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.

Contributor

guykogus commented Oct 9, 2012

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.

@abhinittiwari

This comment has been minimized.

Show comment
Hide comment
@abhinittiwari

abhinittiwari Oct 9, 2012

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

abhinittiwari commented Oct 9, 2012

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

This comment has been minimized.

Show comment
Hide comment
@mattt

mattt Oct 9, 2012

Contributor

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.

Contributor

mattt commented Oct 9, 2012

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

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Oct 9, 2012

Contributor

Try running the tests twice in a row. I.e. when the connection finishes run
it again. On iOS 6 the willCacheResponse method won't be called again if
it's loaded from cache. Interesting difference...

On 9 באוק 2012, at 19:21, Abhinit Tiwari notifications@github.com wrote:

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


Reply to this email directly or view it on
GitHubhttps://github.com/AFNetworking/AFNetworking/issues/566#issuecomment-9270260.

Contributor

guykogus commented Oct 9, 2012

Try running the tests twice in a row. I.e. when the connection finishes run
it again. On iOS 6 the willCacheResponse method won't be called again if
it's loaded from cache. Interesting difference...

On 9 באוק 2012, at 19:21, Abhinit Tiwari notifications@github.com wrote:

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


Reply to this email directly or view it on
GitHubhttps://github.com/AFNetworking/AFNetworking/issues/566#issuecomment-9270260.

@abhinittiwari

This comment has been minimized.

Show comment
Hide comment
@abhinittiwari

abhinittiwari Oct 9, 2012

@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?

abhinittiwari commented Oct 9, 2012

@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?

@MatthewMaker

This comment has been minimized.

Show comment
Hide comment
@MatthewMaker

MatthewMaker commented Oct 9, 2012

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Oct 10, 2012

Contributor

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.

Contributor

guykogus commented Oct 10, 2012

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

This comment has been minimized.

Show comment
Hide comment
@mattt

mattt Oct 31, 2012

Contributor

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.

Contributor

mattt commented Oct 31, 2012

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 mattt closed this Oct 31, 2012

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Nov 1, 2012

Contributor

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 :)

Contributor

guykogus commented Nov 1, 2012

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 :)

@tewha

This comment has been minimized.

Show comment
Hide comment
@tewha

tewha Nov 5, 2012

Contributor

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

Contributor

tewha commented Nov 5, 2012

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

@mattt

This comment has been minimized.

Show comment
Hide comment
@mattt

mattt Nov 5, 2012

Contributor

Yes, thanks for the update @guykogus.

Contributor

mattt commented Nov 5, 2012

Yes, thanks for the update @guykogus.

@magnusottosson

This comment has been minimized.

Show comment
Hide comment
@magnusottosson

magnusottosson Nov 8, 2012

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

magnusottosson commented Nov 8, 2012

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

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Nov 8, 2012

Contributor

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
    ....
}
Contributor

guykogus commented Nov 8, 2012

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
    ....
}
@magnusottosson

This comment has been minimized.

Show comment
Hide comment
@magnusottosson

magnusottosson Nov 8, 2012

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?

magnusottosson commented Nov 8, 2012

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?

@magnusottosson

This comment has been minimized.

Show comment
Hide comment
@magnusottosson

magnusottosson Nov 8, 2012

Is there a way to manually trigger a cached response?

magnusottosson commented Nov 8, 2012

Is there a way to manually trigger a cached response?

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Nov 8, 2012

Contributor

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

Contributor

guykogus commented Nov 8, 2012

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

@magnusottosson

This comment has been minimized.

Show comment
Hide comment
@magnusottosson

magnusottosson Nov 8, 2012

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.

magnusottosson commented Nov 8, 2012

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

This comment has been minimized.

Show comment
Hide comment
@alvinhu

alvinhu Nov 29, 2012

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.

alvinhu commented Nov 29, 2012

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

This comment has been minimized.

Show comment
Hide comment
@Geph0rce

Geph0rce Apr 1, 2013

@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.

Geph0rce commented Apr 1, 2013

@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.

@hpique

This comment has been minimized.

Show comment
Hide comment
@hpique

hpique Mar 16, 2014

Contributor

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

Contributor

hpique commented Mar 16, 2014

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

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Mar 16, 2014

Contributor

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...

Contributor

guykogus commented Mar 16, 2014

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...

@hpique

This comment has been minimized.

Show comment
Hide comment
@hpique

hpique Mar 16, 2014

Contributor

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

Contributor

hpique commented Mar 16, 2014

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

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Mar 16, 2014

Contributor

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

Contributor

guykogus commented Mar 16, 2014

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

@revolter

This comment has been minimized.

Show comment
Hide comment
@revolter

revolter Jun 30, 2015

@guykogus, Do you mean that you don't have to check the NSCachedURLResponse anymore?

revolter commented Jun 30, 2015

@guykogus, Do you mean that you don't have to check the NSCachedURLResponse anymore?

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Jun 30, 2015

Contributor

Try it and find out for yourself :) But my guess is that you don't have to perform the check anymore.

Contributor

guykogus commented Jun 30, 2015

Try it and find out for yourself :) But my guess is that you don't have to perform the check anymore.

@revolter

This comment has been minimized.

Show comment
Hide comment
@revolter

revolter Jun 30, 2015

@guykogus, I did try it for half a day and couldn't get it to work offline, so I did have to perform the check to make it read from cache.

revolter commented Jun 30, 2015

@guykogus, I did try it for half a day and couldn't get it to work offline, so I did have to perform the check to make it read from cache.

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Jun 30, 2015

Contributor

Well I just tested the demo project I used for my bug report with Apple and the issue has been resolved.

Contributor

guykogus commented Jun 30, 2015

Well I just tested the demo project I used for my bug report with Apple and the issue has been resolved.

@revolter

This comment has been minimized.

Show comment
Hide comment
@revolter

revolter Jul 1, 2015

@guykogus, Could you please send me the demo via mail? It would be much appreciated.

revolter commented Jul 1, 2015

@guykogus, Could you please send me the demo via mail? It would be much appreciated.

@guykogus

This comment has been minimized.

Show comment
Hide comment
@guykogus

guykogus Jul 1, 2015

Contributor

@revolter sent :)

Contributor

guykogus commented Jul 1, 2015

@revolter sent :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment