56
votes

Struggling with this problem and loath to implement a custom cookie management system.

It appears some hidden level of iOS's implementation of HTTP fails to manage sessionless cookies properly. Any time an HTTP response sets or deletes a cookie, immediate inspection of NSHTTPCookieStorage cookies will yield the expected results and indicate the correct sessionOnly value.

But if the app quits soon after a response updates cookies, upon relaunch those sessionOnly=FALSE cookies will be reverted to some previous state and the most recent updates lost.

Whether the cookies are set/deleted by a response header or NSHTTPCookieStorage setCookie: makes no difference.

Some caching/syncing voodoo must be going on behind the scenes. The time it takes for the cookie to become persistent can be up to 5 seconds.

ANYONE out there who has or can point to some definitive explanation of this behavior? Is it a bug, plain and simple? Or some undocumented feature whose purpose I can't comprehend?

Some code you can use to reproduce:

- (void)applicationDidBecomeActive:(UIApplication *)application
{

    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];

    NSHTTPCookie *cookie;
    for (cookie in [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies) {
        NSLog(@"%@=%@", cookie.name, cookie.value);
    }

    NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
    [cookieProperties setObject:@"testCookie" forKey:NSHTTPCookieName];
    [cookieProperties setObject:[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] forKey:NSHTTPCookieValue];
    [cookieProperties setObject:@"www.example.com" forKey:NSHTTPCookieDomain];
    [cookieProperties setObject:@"www.example.com" forKey:NSHTTPCookieOriginURL];
    [cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
    [cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion];

    // set expiration to one month from now
    [cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];

    cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];

}

This code should output a new value on every launch. Instead you will see that if you quit the app quickly the value is unchanged.

Some possibly related stack overflow questions:

iphone NSHTTPCookieStorage avaible on app reopen?

iPhone: NSHTTPCookie is not saved across app restarts

NSHTTPCookies refuse to be deleted

deleted NSHTTPCookie returns if app is terminated

3

3 Answers

27
votes

I think the answer lies in one of the SO posts linked to in your question:

I made a sample project to reproduce this issue — and found that it would only occur when the app receives a SIGKILL signal, like when the debugger is stopped from within Xcode. In my experiments, unhandled exceptions, crashes, exit() and abort() don't cause NSHTPPCookieStorage to loose data.

As this looks like a debugging-only issue (it only occurs when using the debugger), I closed the radar I filled previously.

You can test this by restarting the phone normally and observing that all changes to NSHTTPCookieStorage are correctly persisted and reloaded.

15
votes

I also got the same problem but i found a solution. I saved the cookies as it get created by the browser and then recreate them as app restarts.

1) Save cookie when they get created by uiwebview.

 NSMutableArray *cookieArray = [[NSMutableArray alloc] init];
    for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
        [cookieArray addObject:cookie.name];
        NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
        [cookieProperties setObject:cookie.name forKey:NSHTTPCookieName];
        [cookieProperties setObject:cookie.value forKey:NSHTTPCookieValue];
        [cookieProperties setObject:cookie.domain forKey:NSHTTPCookieDomain];
        [cookieProperties setObject:cookie.path forKey:NSHTTPCookiePath];
        [cookieProperties setObject:[NSNumber numberWithInt:cookie.version] forKey:NSHTTPCookieVersion];

        [cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];

        [[NSUserDefaults standardUserDefaults] setValue:cookieProperties forKey:cookie.name];
        [[NSUserDefaults standardUserDefaults] synchronize];

    }

    [[NSUserDefaults standardUserDefaults] setValue:cookieArray forKey:@"cookieArray"];
    [[NSUserDefaults standardUserDefaults] synchronize];

2) Now recreate them as app restarts:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    NSMutableArray* cookieDictionary = [[NSUserDefaults standardUserDefaults] valueForKey:@"cookieArray"];
     NSLog(@"cookie dictionary found is %@",cookieDictionary);

    for (int i=0; i < cookieDictionary.count; i++) 
{


        NSLog(@"cookie found is %@",[cookieDictionary objectAtIndex:i]);
        NSMutableDictionary* cookieDictionary1 = [[NSUserDefaults standardUserDefaults] valueForKey:[cookieDictionary objectAtIndex:i]];
        NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieDictionary1];
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];


 }

    // other code

}

thanks

5
votes

Sounds like an underlying NSUserDefaults-style "synchronize" call is required. I think your best shot is managing all your app's cookies separately in the standard NSUserDefaults and synching any missing ones into the NSHTTPCookieStorage's at startup. Or see if there's some private synchronize method if you're feeling brave :)