Simplifying Facebook iOS SDK

The Problem with Facebook iOS SDK

Facebook+Singleton
Now that the Facebook iOS SDK has been changed to use Graph API, many people are trying to figure out just how to use it properly. In my opinion, however, there are problems with the SDK’s architecture that make it likely to be used poorly. For example, Facebook’s documentation recommends instantiating the Facebook class and authenticating from within your AppDelegate. This is suboptimal for a several of reasons.

First, in many apps that allow Facebook integration, the user may never actually use the Facebook features. Thus, loading the class and authenticating the user is something that should only be done when the user has decided to use a Facebook feature. In other words, we should be lazy loading the Facebook class but their documentation suggests otherwise.

Secondly, the log in method chosen by Facebook (fast-switching the user to the Facebook app or Safari to log in) makes a very poor first impression for your app. Who wants their app to look like it crashed then loaded some Web page the very first time it’s launched?

Lastly, this just begs the programmer to overload the AppDelegate with all kinds of crap or instantiating multiple instances of the Facebook class throughout their app. Facebook’s documentation leaves it to your imagination whether

[facebook requestWithGraphPath:@"me" andDelegate:self];

really means a call to the Facebook instance in your AppDelegate:

Facebook *facebook = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] facebook];
[facebook requestWithGraphPath:@"me" andDelegate:self];

or whether you should be instantiating a new instance of the Facebook class:

Facebook *facebook = [[Facebook alloc] initWithAppId:@"YOUR_APP_ID"];
·
·
·
[facebook requestWithGraphPath:@"me" andDelegate:self];

Neither of which is ideal.

A Better Way

By all rights, the Facebook class should have been implemented as a singleton method. Which simply means there can only be one instance of the class within your application. So I’ve created a category that will make it a singleton method. Likewise, using the class can be simplified greatly by having it maintain it’s own state information rather than cluttering the AppDelegate or requiring the programmer to repeatedly check their NSUserDefaults every time they instantiate the object. So I’ve moved that functionality to the Facebook+Singleton category also.

Using Facebook+Singleton

To use my category, you should get the Facebook iOS SDK from GitHub and add it to your project as usual (See Facebook’s documentation for more details.) Then you’ll need to drag my category files, Facebook+Singleton.h and Facebook+Singleton.m, to your project.

In your AppDelegate, you will want to import Facebook+Singleton.h instead of FBConnect.h, like so:

#import "Facebook+Singleton.h"

Then you will only add the following method to your AppDelegate:

1
2
3
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    return [[Facebook shared] handleOpenURL:url];
}

Note that I’m not using the application:handleOpenURL: as in Facebook’s documentation because that method has been deprecated. This means you should only use that method if your application is targeting an iOS version prior to version 4.2.

Okay, a quick word about why we’re doing this. Didn’t I just say that we shouldn’t clutter the AppDelegate with a bunch of stuff? So why do we need these there?

Since Facebook’s preferred authentication methodology requires leaving your app and launching another app, there needs to be a way for your app to handle the return trip. The application:openURL:sourceApplication:annotation: is that method. So we must have this method in our AppDelegate to handle the authentication when it occurs. However, by using Facebook+Singleton in this way, your app will never instantiate the Facebook class unless it is handling an open URL. Which should normally only occur the very first time a user needs to authenticate with Facebook and never prior to nor again after the first authentication.

That’s all we need in the AppDelegate.

Configuring Your App ID

Next, you’ll need to configure your app id. You’ll need to put it in your info.plist file just like in Facebook’s documentation. But instead of also adding your app id to the AppDelegate, you’ll need to set it in the init method of the Facebook+Singleton.m file instead. Here you’ll place the same app id you would have put in the AppDelegate in the spot where it says YOUR_APP_ID as below:

1
2
3
4
5
6
7
- (id)init {
    if ((self = [super init])) {
        [self initWithAppId:@"YOUR_APP_ID"];
        [self authorize];
    }
    return self;
}

Calling the Graph API

Now, to make calls to the Graph API, you will only need to do the following from any class in your app:

//get information about the currently logged in user
[[Facebook shared] requestWithGraphPath:@"me" andDelegate:self];

//get the logged-in user's friends
[[Facebook shared] requestWithGraphPath:@"me/friends" andDelegate:self];    

//call a legacy REST API
NSMutableDictionary* params = [NSMutableDictionary
    dictionaryWithObjectsAndKeys: @"4", @"uids", @"name", @"fields", nil];

[[Facebook shared] requestWithMethodName: @"users.getInfo"
    andParams: params andHttpMethod: @"GET" andDelegate: self];

Then implement the FBRequestDelegate as usual, specifically the request:didLoad: method for successful requests and request:didFailWithError: method for errors.

Displaying Platform Dialogs

Likewise, with dialogs you’ll need to implement the FBDialogDelegate and call them like:

[[Facebook shared] dialog:@"feed" andDelegate:self];

But Wait, We Didn’t Authorize

Ahhh, this is where the magic starts to appear. With Facebook+Singleton, when the class instantiates, it also automatically authenticates if necessary. However, if you only authenticate in your AppDelegate as Facebook seems to suggest, you are expecting your user to obediently allow your app to access their Facebook account as soon as they launch the app. This is woefully optimistic.

The more non-Facebook functionality your app has, the less likely it is that the user has already allowed it to access Facebook. The last thing you want is for your user to get a “You just defeated the Horrible Dragon. Would you like to share this on Facebook.” message only to discover they can’t share because they didn’t log into Facebook when they first launched the app.

This means you need to ensure that your app can authenticate every time a user decides to begin using a Facebook feature. This is when the existing Facebook iOS SDK becomes a pain because on every page you’ll need to check whether the NSUserDefaults exist, whether a valid session exists, authenticate if not, implement the FBSessionDelegate, and update the NSUserDefaults. Oh yeah, then you should make the call you intended to make. This gets tedious real quick.

The Facebook+Singleton category encapsulates this and does it automatically. The parts that still need to be handled by your class can be handled with NSNotifications.

NSNotifications? What are those?

Since I’ve encapsulated the session states in the Facebook+Singleton category, it is consuming the FBSessionDelegate. It should be the only place that implements the session delegate in your app. You should never set the _sessionDelegate nor call any of the Facebook class methods that implement the id<FBSessionDelegate>, (basically the authorize:delegate:, authorize:delegate:localAppId: and logout: methods.)

Instead, the Facebook class will let your class know about significant session events using NSNotifications. To subscribe to a notification, you should place something like the following in your init or initWithNib: method as appropriate:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(requestFacebookData) name:@"FBDidLogin" object:nil];

In the example above, you have subscribed your class to receive the “FBDidLogin” notification as defined in the Facebook+Singleton category. The “FBDidLogin” gets called once the application has successfully authenticated with Facebook. The selector is the name of any method in your class that you would like to have called when the notification occurs. In this case it is calling a method called requestFacebookData.

You should also unsubscribe any notifications you use in your viewWillDisappear: method like this:

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"FBDidLogin" object:nil];

So, for example, if you have a UITableView that should display a list of your user’s friends, the code could look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#import "Facebook+Singleton.h"

- (id)init {
    if ((self = [super init])) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(requestFacebookData) name:@"FBDidLogin" object:nil];
    }
    return self;
}

- (void)viewDidLoad {
        [self requestFacebookData];
}

- (void)requestFacebookData {
        [[Facebook shared] requestWithGraphPath:@"me/friends" andDelegate:self];
}

- (void)viewWillDisappear:(BOOL)animated {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:@"FBDidLogin" object:nil];
}

Of course, you’ll still need to implement the FBRequestDelegate as usual, specifically the request:didLoad: method for successful requests and request:didFailWithError: method for errors. As you can see, this is a much simpler way to implement the Facebook class than is originally provided by the Facebook iOS SDK.

What Notifications Are Available?

The notifications posted by the Facebook+Singleton category are:

  • FBDidLogin—a successful authentication has occurred.
  • FBDidLogout—the user is successfully logged out.
  • FBDidNotLogin—a login is unsuccessful and also calls one of:
    • FBLoginCancelled—when a user cancels.
    • FBLoginFailed—failed for any other reason.

Another Word about Authentications

The method the Facebook iOS SDK uses for authentication is somewhat annoying. It exits your app and loads either the Facebook app or Safari to handle the authentication. The reason Facebook uses this method is because any app running on the iPhone is sandboxed to keep apps from maliciously using the UIWebView to use your credentials on other sites. However, this also means the cookies used for tracking sessions on Facebook are not available to your app. To get around this, Facebook prefers to exit your app and launch into another app which may likely already have an active Facebook session to keep the user from needing to type in their username and password.

However, the authorization process is infrequent. It only needs to be done once from each app (unless the app is deleted and reinstalled or otherwise logged out) so it is not a huge burden to require the user to type in their credentials. When handled from within your app, though, it looks much cleaner to the user than the fast app switching employed in the Facebook iOS SDK. In my opinion, Facebook’s preferred authentication makes your app look clumsy and amateurish. For that reason, I have also enabled you to change the login behavior when using the Facebook+Singleton category.

If you look within the Facebook+Singleton.m file, you will see that there is an authorize method which calls one of the following methods:

  • authorize:localAppId: The standard authentication used by Facebook which exits your app to login via the Facebook app or Safari.
  • authorizeInApp:localAppId: Pops up a standard FBDialog, which is simply a UIWebView, and never exits your app. If you use this method the user must type in an email/phone number and password when they first authenticate. Additionally, you will not need any of the previously mentioned code in your AppDelegate.
  • authorizeWithFacebookApp:localAppId: – Only leaves your app if the user has the Facebook app installed.

You can choose to use any of these methods in your app by uncommenting the line and commenting the others. Likewise, if you need to specify permissions or add a local app id you should do so here. If you don’t know what that means, just leave them set to nil.

Download Facebook+Singleton and Try It Out

I would like to thank you for taking a look at the Facebook+Singleton category and I hope you find it useful for your project. You can download them below.

As time permits, I will be releasing other Facebook related classes that build upon the Facebook+Singleton category.

Facebook+Singleton.h

Facebook+Singleton.m

This entry was posted in Facebook iOS SDK, iOS Programming and tagged , , , . Bookmark the permalink. Follow any comments here with the RSS feed for this post. Trackbacks are closed, but you can post a comment.

5 Comments

  1. kenyi
    Posted August 22, 2011 at 12:29 am | Permalink

    Thank you. You saved me from hours of research!

  2. lior
    Posted September 13, 2011 at 7:08 pm | Permalink

    Thanks for that class. it’s awesome!

    I got one question, I want to let the user “change user” – which means to logout and then login again with a different user. How should I call “logout” and “login”?
    Thanks

    • Posted September 14, 2011 at 2:11 pm | Permalink

      First, you’ll want to subscribe to the FBDidLogout notification.

      Then, you would call logout: to log the user out then wait for the FBDidLogout notification.

      Finally, call to authorize: which should take the user to a new login.

      • Posted July 25, 2012 at 10:26 pm | Permalink

        You say here that we would call logout: and then autorize: but in your post you said “You should never set the _sessionDelegate nor call any of the Facebook class methods that implement the id, (basically the authorize:delegate:, authorize:delegate:localAppId: and logout: methods.)”

  3. Posted July 19, 2012 at 12:18 pm | Permalink

    Thankssssssss!!

    This post is so well explained, I almost want to pay you for what you wrote. Nobody explained so good as you did here. Thanks again. I will try to implement it, and I’ll pass you the github repo link.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

  • GitHub Projects

  • T-Shirts

Privacy Policy    Terms of Service