Facebook Tutorial for iOS: How To Use Facebook’s New Graph API from your iPhone App
A Facebook tutorial in which you will learn how to use Facebook’s Graph API from your iPhone app to authenticate the user, post to their wall, and more. By Ray Wenderlich.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Facebook Tutorial for iOS: How To Use Facebook’s New Graph API from your iPhone App
20 mins
If you are an iPhone developer and want to integrate Facebook into your app, things can be a bit confusing these days.
First, there’s this iPhone library called Facebook Connect that looks pretty cool – but then there’s this other thing called the Graph API too.
So what’s this Graph API thing all about, and how does it relate to Facebook Connect?
Short answer: Facebook Connect uses the old API, but the Graph API is the new API. The new Graph API is simple, has more flexibility, and you can do things with it that you couldn’t do before.
But Facebook Connect doesn’t use the Graph API, so what is a poor developer to do?
Update: Since writing this Facebook tutorial, Facebook Connect now supports the Graph API. However, this Facebook tutorial may still be useful if you want to see how things work under the hood!
That’s where this Facebook tutorial series comes in! In this three-part series, we will cover how the Graph API works step by step, including:
- How to authenticate the user with the Graph API
- How to get user information
- How to post to their wall
- How to upload photos
- And even how to add a “like button” to your Facebook fan page!
In this first article, we’ll mainly cover how to authenticate the user using the Graph API, since that’s really the hardest part (the rest is pretty simple). (Jump to Part 2 or Part 3 of the series.) So let’s get started by talking about how the authentication works!
How OAuth Authentication Works
To authenticate users to Facebook, the Graph API uses the new OAuth 2.0 protocol. Don’t worry – the name sounds imposing, but the way it works is actually pretty simple.
The idea is you make an app using the Facebook developer’s portal, and when you do you get a unique ID for your app called an API Key.
Then when you want to log a user into Facebook, you go to the following URL in a web browser:
https://graph.facebook.com/oauth/authorize? client_id=[your API key]& redirect_uri=http://www.facebook.com/connect/login_success.html& scope=[extended permissions you want]& type=user_agent& display=touch
The user will see a standard Facebook login page and they can enter in their username and password, and give any permissions required.
You don’t need to implement a redirect_uri yourself – you can just use a preset one from Facebook, as shown above. The preset callback will return to you an access token, which is what you’ll need to make all future requests.
When a user logs in, Facebooks stores some information about the login in cookies. So next time you try to login, if the cookies haven’t expired, the login will automatically complete without user intervention!
Where’s the Secret Key?
If you’re familiar with using the old Facebook Connect API, you’ll notice that nowhere in the above steps do you pass your app’s secret key.
This arguably is a security issue, but its effect is mitigated on the iPhone by the fact that Safari stores cookies on a per-app basis, and the user would probably notice if your app was trying to log in as a different app.
This is not really within the scope of this blog post so I’m not going to discuss it futher here, but if you are curious you can check out this Facebook forum post on the matter or contact me.
IMHO, on the iPhone it isn’t a huge deal so I still plan on using Facebook & the Graph API in my apps. But I am going to be very careful what I put on Facebook and what apps I allow to access it, as all of us should already know! :]
Anyway – enough background, let’s get this working in some code!
Creating a Login Dialog
The first thing we need to do is create a web view that we can redirect to the OAuth authentication URL shown above.
Since the user might not have to manually login if cookies from an old login are still valid, we will try logging in behind the scenes, and only show the web view if a manual login is required.
Let’s start by making a new project. Go to File\New Project, choose Application\View-based Application, and click “Choose…”. Name the project FBFun, and click “Save”.
Then click on File\New File, choose iPhone OS\Cocoa Touch Class\UIViewController subclass, make sure “With XIB for user interface” is checked, and click “Next”. Name the file “FBFunLoginDialog’, and click “Finish”.
Open up FBFunLoginDialog.h and replace the contents with the following:
#import <UIKit/UIKit.h>
@protocol FBFunLoginDialogDelegate
- (void)accessTokenFound:(NSString *)accessToken;
- (void)displayRequired;
- (void)closeTapped;
@end
@interface FBFunLoginDialog : UIViewController <UIWebViewDelegate> {
UIWebView *_webView;
NSString *_apiKey;
NSString *_requestedPermissions;
id <FBFunLoginDialogDelegate> _delegate;
}
@property (retain) IBOutlet UIWebView *webView;
@property (copy) NSString *apiKey;
@property (copy) NSString *requestedPermissions;
@property (assign) id <FBFunLoginDialogDelegate> delegate;
- (id)initWithAppId:(NSString *)apiKey
requestedPermissions:(NSString *)requestedPermissions
delegate:(id<FBFunLoginDialogDelegate>)delegate;
- (IBAction)closeTapped:(id)sender;
- (void)login;
- (void)logout;
-(void)checkForAccessToken:(NSString *)urlString;
-(void)checkLoginRequired:(NSString *)urlString;
@end
The first thing we do here is create a delegate, which FBFunViewController will eventually implement. We want to be able to tell this guy if we’ve found an access token or if he needs to display us on the screen.
Next, we set up a couple member variables. We need to keep track of our web view, and also store the API key and any permissions that our app wants. And of course, we need a reference to the delegate.
At the end, we just declare the methods that we’ll be implementing in our .m file later.
Ok – time to set up the UI! Open up FBFunLoginDialog.xib, drag a UIWebView to the center of the view, and resize it a bit so there’s some space at the bottom. Add a button into the bottom that says “Close”. When you’re done it should look like the following:
Then control-drag from “File’s Owner” to the UIWebView and connect it to the “webView” outlet. Also control-drag from the UIWebView back up to the “File’s Owner”, and connect it to the “delegate” outlet. Finally, control-drag from the UIButton up to “File’s Owner”, and connect it to the “closeTapped:” outlet.
That’s it with Interface Builder – save your XIB and head over to FBFunLoginDialog.m!
Loading the Authorization URL
The first thing we’re going to do is load the Authorization URL to get the process rolling. But first we have to take care of some member variables, so start by adding the following to the top of the file:
#import "FBFunLoginDialog.h"
@implementation FBFunLoginDialog
@synthesize webView = _webView;
@synthesize apiKey = _apiKey;
@synthesize requestedPermissions = _requestedPermissions;
@synthesize delegate = _delegate;
- (id)initWithAppId:(NSString *)apiKey
requestedPermissions:(NSString *)requestedPermissions
delegate:(id<FBFunLoginDialogDelegate>)delegate {
if ((self = [super initWithNibName:@"FBFunLoginDialog"
bundle:[NSBundle mainBundle]])) {
self.apiKey = apiKey;
self.requestedPermissions = requestedPermissions;
self.delegate = delegate;
}
return self;
}
- (void)dealloc {
self.webView = nil;
self.apiKey = nil;
self.requestedPermissions = nil;
[super dealloc];
}
Pretty standard stuff here – we just set up an initializer which FBFunViewController will call to create the dialog, and synthesize/dealloc our variables.
Next add the login/logout methods that FBFunViewController will call when the user wants to login or logout from Facebook:
- (void)login {
[_webView loadRequest:
[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
NSString *redirectUrlString =
@"http://www.facebook.com/connect/login_success.html";
NSString *authFormatString =
@"https://graph.facebook.com/oauth/authorize?
client_id=%@&redirect_uri=%@&scope=%@&type=user_agent&display=touch";
NSString *urlString = [NSString stringWithFormat:authFormatString,
_apiKey, redirectUrlString, _requestedPermissions];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[_webView loadRequest:request];
}
-(void)logout {
NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie* cookie in
[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
[cookies deleteCookie:cookie];
}
}
In the login method, the first thing we do is clear the web view by loading a blank about page, just so there’s nothing left from any previous loads.
Then, we set up our URL string by replacing the client id and requested permissions with the values we have stored, and tell our web view to load that page.
In the logout method, we just clear all the cookies. It’s OK to clear all of them, because cookies are stored on a per-app basis, and in this app we’re only using the web browser for Facebook. If we were using several sites, it might be better to just clear out Facebook’s cookies by examining the cookie’s domain.
Next up, since we set ourselves as the delegate of the UIWebView we’ll get notifications when the web view starts and finishes loading URLs. In our case what we care about is when the web view begins to load a URL, so add this next:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *urlString = request.URL.absoluteString;
[self checkForAccessToken:urlString];
[self checkLoginRequired:urlString];
return TRUE;
}
In the above, when the web browser begins to load a URL, we check for the two cases we care about:
- The login completed successfully, and Facebook called the premade redirect URL (checkForAccessToken).
- Facebook needs the user to enter in their username and password (checkLoginRequired).
Checking for the Access Token
Let’s write the function to check for the access token in the URL next:
-(void)checkForAccessToken:(NSString *)urlString {
NSError *error;
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:@"access_token=(.*)&"
options:0 error:&error];
if (regex != nil) {
NSTextCheckingResult *firstMatch =
[regex firstMatchInString:urlString
options:0 range:NSMakeRange(0, [urlString length])];
if (firstMatch) {
NSRange accessTokenRange = [firstMatch rangeAtIndex:1];
NSString *accessToken = [urlString substringWithRange:accessTokenRange];
accessToken = [accessToken
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[_delegate accessTokenFound:accessToken];
}
}
}
If a login is successful, the web browser will eventually be redirected to a URL that looks like the following:
http://www.facebook.com/connect/login_success.html#access_token=...&...
So in checkForAccessToken, we want to check to see if the URL contains the letters “access_token=” eventually followed by an ampersand, and grab everything in between.
Of course we could do this with some regular string parsing, but I thought this would be a good opportunity to demonstrate the new NSRegularExpression API introduced in iOS 3.2 that is really helpful for stuff like this.
First we create a NSRegularExpression object, passing in a regular expression pattern. Explaining regular expressions is outside of the scope of this article, but if you want to learn more, check out regular-expressions.info.
But in short, the above pattern is saying:
- Search for anything that contains the letters “access_token”…
- …Then any amount of any type of characters…
- …But stop when we see an ampersand!
- And btw, store the stuff between access_token and the ampersand somewhere so we can grab it later, since it’s marked with parenthesis.
We then call firstMatchInString to check if there’s a matchm, and pull out the first “submatch” (rangeAtIndex:1) to get the access token. We then need to remove the escaped characters in the access token to get the actual value, and then pass that onto our delegate.
So why did we just do all of that work? Again, the access token is what we’ll need in all future calls to the Graph API, such as posing to the wall, uploading photos, etc.
Checking If Manual Login is Required
Two last functions to add:
-(void)checkLoginRequired:(NSString *)urlString {
if ([urlString rangeOfString:@"login.php"].location != NSNotFound) {
[_delegate displayRequired];
}
}
- (IBAction)closeTapped:(id)sender {
[_delegate closeTapped];
}
For checkLoginRequired, we simply check if the “login.php” page shows up in the URL, which appears to work in practice for me. Not sure if there’s a more elegant way to do it than that though.
Finally, if the user taps the close button, we notify our delegate too so he can remove us from display.
Creating Our Main View
Now that we have this fancy Facebook Login Dialog, let’s use it! Open up FBFunViewController.h and replace its contents with the following:
#import <UIKit/UIKit.h>
#import "FBFunLoginDialog.h"
typedef enum {
LoginStateStartup,
LoginStateLoggingIn,
LoginStateLoggedIn,
LoginStateLoggedOut
} LoginState;
@interface FBFunViewController : UIViewController <FBFunLoginDialogDelegate> {
UILabel *_loginStatusLabel;
UIButton *_loginButton;
LoginState _loginState;
FBFunLoginDialog *_loginDialog;
UIView *_loginDialogView;
}
@property (retain) IBOutlet UILabel *loginStatusLabel;
@property (retain) IBOutlet UIButton *loginButton;
@property (retain) FBFunLoginDialog *loginDialog;
@property (retain) IBOutlet UIView *loginDialogView;
- (IBAction)loginButtonTapped:(id)sender;
@end
Nothing too fancy here. We just create an enum to help us keep track of what state we’re currently in, create a bunch of member variables to keep UI elements that we’ll need, and create an outlet for when the user clicks the login/logout button.
Next open up FBFunViewController.xib and drag a UILabel and a UIButton into the view. Then click Layout\Show Bounds Rectangles to turn on a blue border around each of these, which will make positioning easier. Delete the default text for the button and label, and arrange them on the screen to look similar to the following:
Then control-drag from “File’s Owner” to the UIButton and UILabel, connecting them to the “loginButton” and “loginStatusLabel”, respectively. Also control-drag from the UIButton back up to the “File’s Owner”, and connect it to the “loginButtonTapped” delegate.
Save your XIB, and let’s head over to FBFunViewController.m to write the implementation. Again there’s a bunch of code here so let’s break it into parts. Start with the following:
#import "FBFunViewController.h"
@implementation FBFunViewController
@synthesize loginStatusLabel = _loginStatusLabel;
@synthesize loginButton = _loginButton;
@synthesize loginDialog = _loginDialog;
@synthesize loginDialogView = _loginDialogView;
- (void)dealloc {
self.loginStatusLabel = nil;
self.loginButton = nil;
self.loginDialog = nil;
self.loginDialogView = nil;
[super dealloc];
}
- (void)refresh {
if (_loginState == LoginStateStartup || _loginState == LoginStateLoggedOut) {
_loginStatusLabel.text = @"Not connected to Facebook";
[_loginButton setTitle:@"Login" forState:UIControlStateNormal];
_loginButton.hidden = NO;
} else if (_loginState == LoginStateLoggingIn) {
_loginStatusLabel.text = @"Connecting to Facebook...";
_loginButton.hidden = YES;
} else if (_loginState == LoginStateLoggedIn) {
_loginStatusLabel.text = @"Connected to Facebook";
[_loginButton setTitle:@"Logout" forState:UIControlStateNormal];
_loginButton.hidden = NO;
}
}
- (void)viewWillAppear:(BOOL)animated {
[self refresh];
}
This is just the boilerplate code to synthesize and deallocate our variables. We also create a refresh method we can call to update our labels and buttons with text to represent our current state.
Calling the Login Dialog
Next add the implementation for when the login button is tapped:
- (IBAction)loginButtonTapped:(id)sender {
NSString *appId = @"8fa673a06891cac667e55d690e27ecbb";
NSString *permissions = @"publish_stream";
if (_loginDialog == nil) {
self.loginDialog = [[[FBFunLoginDialog alloc] initWithAppId:appId
requestedPermissions:permissions delegate:self] autorelease];
self.loginDialogView = _loginDialog.view;
}
if (_loginState == LoginStateStartup || _loginState == LoginStateLoggedOut) {
_loginState = LoginStateLoggingIn;
[_loginDialog login];
} else if (_loginState == LoginStateLoggedIn) {
_loginState = LoginStateLoggedOut;
[_loginDialog logout];
}
[self refresh];
}
First up is our app id, which you get from the Facebook developer’s portal when you create your app. The id in the code is the same ID from the “My Grades” test app we made in the Facebook Connect tutorial. Feel free to use this, or replace it with your own.
Next up we set what extended permissions we want. You can read up on the extended permissions that are available, but for now just know that this is the one you use if you want to post to a user’s wall, or upload photos.
Next we check to see if the login dialog has been created, and if not create it passing in our app id and permissions. Note we also store a reference to the login dialog view, which is retained. You might say to your self: WTFBBQ?
Well, since the view controller is in the background, usually its viewDidLoad will not be called until it is displayed (because that is usually the first time the views are actually needed). This is not good for us, because we want the view (and hence the web view) to be loaded even if it isn’t being displayed, so that it can do some work in the background.
So to trigger that to happen, we just store a reference to the view. Don’t know if there is a better/more elegant way to do that, but this is one way that works.
Next, we check our login state, and either call login or logout on the dialog. We also update our state accordingly, and refresh our display.
Implementing FBFunLoginDialogDelegate
A couple more functions to add and then we’re done: the implementation of the FBFunLoginDialogDelegate!
- (void)accessTokenFound:(NSString *)accessToken {
NSLog(@"Access token found: %@", accessToken);
_loginState = LoginStateLoggedIn;
[self dismissModalViewControllerAnimated:YES];
[self refresh];
}
- (void)displayRequired {
[self presentModalViewController:_loginDialog animated:YES];
}
- (void)closeTapped {
[self dismissModalViewControllerAnimated:YES];
_loginState = LoginStateLoggedOut;
[_loginDialog logout];
[self refresh];
}
In accessTokenFound, all we do is print out the access token and dismiss the dialog. Now that we have the access token, we could do all kind of fun things, but this blog post has gone on more than long enough already so we’ll save that for next time :]
In displayRequired, we just present the login dialog so the user can enter their username and password, and in closeDialog we just shut things down.
And that’s it! Compile and run the app, and you should be able to log in and out of Facebook:
You can also see that it will automatically log you in if you log in, exit the app, restart and log in again, since the cookies will have been saved for your session. However – it appears that the web view must save the cookies periodically in the background, so you’ll have to wait a minute or so after you log in for the cookies to be persisted to disk for it to work.
Where To Go From Here?
Here is a sample project with all of the code we developed in the above Facebook tutorial.
Now that we have a login token, check out the next Facebook tutorial in the series, where we’ll cover how you can use it to get all kinds of interesting data from Facebook – including whether you prefer babes or dudes!
Please comment below if you use or are planning on using the new Facebook Graph API in your Facebook apps – or if you’re just planning on sticking with Facebook Connect for now!