Saving Sessions with the Facebook iOS SDK

by Chadwick Wood
January 5th, 2011

Recently I added Facebook image sharing to Glory Math 1, and it was my first experience with the Facebook iOS SDK. I used the newer Sign Sign-On feature, which briefly takes the user to the Facebook app to ask them whether your app can access their Facebook profile. The docs are good, and the whole process was pretty straightforward, but one aspect of the authentication process really annoys me: Every time my app launches again, the user must go through the authentication process with Facebook. The first time the user authenticates, it'll ask for access, but every time after that, it just tells the user that they already approved the application for access, and they just have to tap "Okay". This process seems pointless to me. So, I tried to find a way around it. The cause of having to go through the sign on process every time the app is re-launched is that, after successfully signing on the first time, the Facebook SDK doesn't save the access token it receives in a persistent manner. So, when the app quits, your token is gone. This is easy enough to get around. Rather than create my own example project to demonstrate, I'll just specify how you would change the Facebook Demo App to store the access token. All it takes are changes to a couple of methods in DemoAppViewController.m:

// around line 25
static NSString* kAppId = nil;
// use these as the keys for storing the token and date in user defaults
#define ACCESS_TOKEN_KEY @"fb_access_token"
#define EXPIRATION_DATE_KEY @"fb_expiration_date"


/**
 * Called when the user has logged in successfully.
 */
// around line 193
- (void)fbDidLogin {
    [self.label setText:@"logged in"];
    _getUserInfoButton.hidden = NO;
    _getPublicInfoButton.hidden = NO;
    _publishButton.hidden = NO;
    _uploadPhotoButton.hidden = NO;
    _fbButton.isLoggedIn = YES;
    [_fbButton updateImage];

    // store the access token and expiration date to the user defaults
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:_facebook.accessToken forKey:ACCESS_TOKEN_KEY];
    [defaults setObject:_facebook.expirationDate forKey:EXPIRATION_DATE_KEY];
    [defaults synchronize];
}

/**
 * Show the authorization dialog.
 */
// around line 88
- (void)login {
    // on login, use the stored access token and see if it still works
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    _facebook.accessToken = [defaults objectForKey:ACCESS_TOKEN_KEY];
    _facebook.expirationDate = [defaults objectForKey:EXPIRATION_DATE_KEY];

    // only authorize if the access token isn't valid
    // if it *is* valid, no need to authenticate. just move on
    if (![_facebook isSessionValid]) {
        [_facebook authorize:_permissions delegate:self];
    }
}

Making these few small changes will store the access token and attempt to re-use it.

Better, But Still Annoying

There's a big gotcha, though: the expiration date that Facebook gives the token appears to be about 24 hours from when it was issued. So, this technique will only save the user from repeat authentications in a 1-day period. It's an improvement, but still pretty annoying. Maybe down the road Facebook will start issuing tokens that work for longer. Also, I've only tested this when requesting the publish_stream privilege for an application. I'm wondering if maybe the token you get when requesting offline access might have a longer life.

If you've had any different experience, or know of a better way to not force the user to authenticate over and over, let me know in the comments.