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.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

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:

  1. The login completed successfully, and Facebook called the premade redirect URL (checkForAccessToken).
  2. 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:

Layout for our Main Facebook View Controller

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.

Contributors

Over 300 content creators. Join our team.