Matt Swain

Facebook iOS SDK Singleton


Update (2012-10-04)

This isn’t really relevant anymore with the latest version of the Facebook iOS SDK.


Last year, Barry Murphy shared a great Objective-C category that extends the Facebook iOS SDK class to make it a singleton object that can be easily accessed from anywhere within your app without polluting your AppDelegate.

Recent changes to the Facebook iOS SDK mean that a lot of the methods needed renaming, and I wanted to use it in a project with automatic reference counting (ARC), which required a few changes to the singleton init method.

Here’s what I came up with:

Facebook+Singleton.h

#import "FBConnect.h"
#define kFBAccessTokenKey  @"FBAccessTokenKey"
#define kFBExpirationDateKey  @"FBExpirationDateKey"

@interface Facebook (Singleton) <FBSessionDelegate>
- (void)authorize;
+ (Facebook *)shared;
@end

Facebook+Singleton.m

#import "Facebook+Singleton.h"

@implementation Facebook (Singleton)

- (id)init {
    if ((self = [self initWithAppId:@"<APP-ID-HERE>" andDelegate:self])) {
        [self authorize];
    }
    return self;
}

- (void)authorize {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    if ([defaults objectForKey:kFBAccessTokenKey] && [defaults objectForKey:kFBExpirationDateKey]) {
        self.accessToken = [defaults objectForKey:kFBAccessTokenKey];
        self.expirationDate = [defaults objectForKey:kFBExpirationDateKey];
    }

    if (![self isSessionValid]) {
        NSArray *permissions =  [NSArray arrayWithObjects:@"email", @"user_about_me", nil];
        [self authorize:permissions];
    }
}

- (void)fbDidLogin {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:[self accessToken] forKey:kFBAccessTokenKey];
    [defaults setObject:[self expirationDate] forKey:kFBExpirationDateKey];
    [defaults synchronize];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"FBDidLogin" object:self];
}

- (void)fbDidNotLogin:(BOOL)cancelled {
    if (cancelled) {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"FBLoginCancelled" object:self];
    } else {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"FBLoginFailed" object:self];
    }
    [[NSNotificationCenter defaultCenter] postNotificationName:@"FBDidNotLogin" object:self];
}

- (void)fbDidLogout {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObjectForKey:kFBAccessTokenKey];
    [defaults removeObjectForKey:kFBExpirationDateKey];
    [defaults synchronize];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"FBDidLogout" object:self];
}

static Facebook *shared = nil;

+ (Facebook *)shared {
    @synchronized(self) {
        if(shared == nil)
            shared = [[self alloc] init];
    }
    return shared;
}

Read more at Barry’s post on how to use this and the rationale behind this approach.