16
votes

I'm developing an application where the server hands me a cookie to identify the user.

My successive requests need to have that cookie to have the response that the user expects. What I can't understand is how and when the shared instance of NSHTTPCookieStorage loses its cookies.

The first solution I used is to archive and save the cookies from my server to user defaults on application terminations then clear existing ones from my server on application launch and reset the ones I stored. Through the developing process I didn't encounter problems as the sessions for debugging are very short and didn't usually need to put the app in background.

During beta testing, troubles started. The hack I came with is to save the cookies not only on application termination but also after the API calls that hand me back these cookies. And to load the saved cookies not only on app launch but also when the app returns to foreground.

How come the NSHTTPCookieStorage share instance gets rid of these cookies and what's the best practice to deal with it as it's a vital part of my app and I can't trust such a hacked solution if not backed up by more experienced developers.

Thank you in advance for your answers

EDIT: Here are the methods to save/read/clear the cookies

-(void)saveStoredCookies
{
    NSURL *httpUrl = @"http://myServer.com";
    NSURL *httpsUrl = @"https://myServer.com";

    NSArray *httpCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:httpUrl];
    NSData *httpCookiesData = [NSKeyedArchiver archivedDataWithRootObject:httpCookies];
    [[NSUserDefaults standardUserDefaults] setObject:httpCookiesData forKey:@"savedHttpCookies"];

    NSArray *httpsCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:httpsUrl];
    NSData *httpsCookiesData = [NSKeyedArchiver archivedDataWithRootObject:httpsCookies];
    [[NSUserDefaults standardUserDefaults] setObject:httpsCookiesData forKey:@"savedHttpsCookies"];

    [[NSUserDefaults standardUserDefaults] synchronize];
}

-(void)readStoredCookies
{
    //clear, read and install stored cookies
    NSURL *httpUrl = @"http://myServer.com";
    NSURL *httpsUrl = @"https://myServer.com";

    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:httpUrl];
    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
    }
    cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:httpsUrl];
    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
    }

    NSData *httpCookiesData = [[NSUserDefaults standardUserDefaults] objectForKey:@"savedHttpCookies"];
    if([httpCookiesData length]) {
        NSArray *savedCookies = [NSKeyedUnarchiver unarchiveObjectWithData:httpCookiesData];
        for (NSHTTPCookie *cookie in savedCookies) {
            [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
        }       
    }
    NSData *httpsCookiesData = [[NSUserDefaults standardUserDefaults] objectForKey:@"savedHttpsCookies"];
    if([httpsCookiesData length]) {
        NSArray *savedCookies = [NSKeyedUnarchiver unarchiveObjectWithData:httpsCookiesData];
        for (NSHTTPCookie *cookie in savedCookies) {
            [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
        }       
    }
}

-(void)clearStoredCookies
{
    NSURL *httpUrl = @"http://myServer.com";
    NSURL *httpsUrl = @"https://myServer.com";
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:httpUrl];
    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
    }
    cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:httpsUrl];
    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
    }
}
4
Please update with the code you are using to pull the cookies out of cookie storage upon a new request. your description is not sufficient.deleted_user
You didnt see a problem because you werent loading the cookies from storage between new sessions during testing.deleted_user

4 Answers

21
votes

NSHttpCookieStorage loses its cookies because you didn't set the expiration time for cookies. Setting expiration time is necessary otherwise your cookies will lose when your app exits.

Here is a quick look how I stored my cookies during app exit and launch,

NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
[cookieProperties setObject:name forKey:NSHTTPCookieName];
[cookieProperties setObject:strValue forKey:NSHTTPCookieValue];
[cookieProperties setObject:@"myserver.com" forKey:NSHTTPCookieDomain];    // Without http://
[cookieProperties setObject:@"myserver.com" forKey:NSHTTPCookieOriginURL]; // Without http://
[cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];

// set expiration to one month from now or any future NSDate of your choice
// this makes the cookie sessionless and it will persist across web sessions and app launches
/// if you want the cookie to be destroyed when your app exits, don't set this
[cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];

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

Hope this helps.

7
votes

Is this the simulator? And using rolling cookies?

In the simulator, cookies don't really persist as they should, because they're shared with desktop Safari. As the cookies roll over, one eventually overwrites the other.

This isn't a problem on the device itself, where each app has its own cookie store. (And, truth be told, I haven't noticed it happening yet in Mountain Lion.)

1
votes

humayun might be right -

However you should not be writing cookies to NSUserDefaults -

The whole point of the sharedHTTPCookieStorage is you read them from there.

NSArray *httpCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:httpUrl];

You should not even be able to write those cookies to NSUserDefaults for security reasons. But maybe you can. Either way its probable that the timeout wasnt set as humayun mentioned.

Either way please refactor your code and dont try to store cookies in user prefs.

0
votes

Solved !

After some simple debugging I found out that the problem was with the url I was using in cookiesForURL:. I simply started using cookies property and now it works fine.