Sign up for a Parse account to implement this tutorial and more!

Sign Up

Icon_facebook_ios
Integrating Facebook in iOS

Learn how to use Parse with the Facebook SDK to create a profile viewer application. This tutorial will teach you how to create and login Parse users through Facebook and make queries to the Facebook Graph API.

iOS
Facebook
PFUser

Download code for this tutorial:

.zip File GitHub

Parse makes it very easy for you to quickly add user accounts to your application using the PFUser object. But, if you want to integrate your application with Facebook, it can be challenging to manage two sets of users. The Facebook SDK can be used with Parse's SDK, and is integrated with the PFUser class to make linking your users to their Facebook identities easy.

This tutorial will guide you through the creation of a simple iOS application that allows a user to log in via Facebook and view their profile information. It consists of two view controllers. The LoginViewController has a single button that allows the user to log in, and the UserDetailsViewController presents the user's profile information in a UITableView. You can download the Xcode project from the link above.

Prerequisites

To start using Facebook with Parse, you need to:

Setup

We'll start by configuring the application for use with Parse and Facebook. First, add your Parse application and client keys in the app delegate. Then, select your project file and navigate to the "Info" tab. Add a new key for FacebookAppID under "Custom iOS Target Properties" with your Facebook app's ID as a string value. Next you will need to add a "URL Type" to your project. Using the "Add" button on the bottom right, select "Add URL Type". You should see a new URL Type called "untitled". Expand it and set the URL Scheme field to "fbYour_App_Id" (ex. fb1234567890).

Now that all of the keys are in place, we need to add a few methods to the app delegate to support the Single-Sign On feature of the Facebook SDK.

- (BOOL)application:(UIApplication *)application 
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
    return [FBAppCall handleOpenURL:url
                  sourceApplication:sourceApplication
                        withSession:[PFFacebookUtils session]];
}
 
- (void)applicationDidBecomeActive:(UIApplication *)application {
    [FBAppCall handleDidBecomeActiveWithSession:[PFFacebookUtils session]];
}

Login

When you use the Parse framework to log in a new user through Facebook, a User object is also created and stored for you in Parse. You are still able to set and retrieve data as you would with a regular PFUser object, but you also have access to the entire Facebook API. In the loginButtonTouchHandler: method within the LoginViewController we can log in the user as follows. If you’ve used the PFUser object before, this should look very familiar, except that you're making a call to PFFacebookUtils.

- (IBAction)loginButtonTouchHandler:(id)sender  {
    // The permissions requested from the user
    NSArray *permissionsArray = @[ @"user_about_me", @"user_relationships", @"user_birthday", @"user_location"];
    
    // Login PFUser using Facebook
    [PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
        [_activityIndicator stopAnimating]; // Hide loading indicator
        
        if (!user) {
            if (!error) {
                NSLog(@"Uh oh. The user cancelled the Facebook login.");
            } else {
                NSLog(@"Uh oh. An error occurred: %@", error);
            }
        } else if (user.isNew) {
            NSLog(@"User with facebook signed up and logged in!");
            [self.navigationController pushViewController:[[UserDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped] animated:YES];
        } else {
            NSLog(@"User with facebook logged in!");
            [self.navigationController pushViewController:[[UserDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped] animated:YES];
        }
    }];
}

Notice that we create a permissions array. This indicates what data we want to access once the user is logged in. The full list can be found on the Facebook developer website.

Requesting and Setting User Data

Now that the user is logged in, we can start making requests to the Facebook Graph API. To make our request as soon as the view is displayed, we add this code to the UserDetailsViewController's viewDidLoad method.

- (void)viewDidLoad {
    // ...
    // Create request for user's Facebook data
    FBRequest *request = [FBRequest requestForMe];

    // Send request to Facebook
    [request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
        // handle response
    }];
}

The user's Facebook access token will be automatically added to the request, so a "me"-based request path will return the current user's data.

To handle the reply from the graph request, we can parse through the received data inside the FBRequest completion handler. We can use this response to populate the appropriate UI elements.

- (void)viewDidLoad {
    // ...
    // Create request for user's Facebook data
    FBRequest *request = [FBRequest requestForMe];

    // Send request to Facebook
    [request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
        if (!error) {
            // result is a dictionary with the user's Facebook data
            NSDictionary *userData = (NSDictionary *)result;

            NSString *facebookID = userData[@"id"];
            NSString *name = userData[@"name"];
            NSString *location = userData[@"location"][@"name"];
            NSString *gender = userData[@"gender"];
            NSString *birthday = userData[@"birthday"];
            NSString *relationship = userData[@"relationship_status"];

            NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large&return_ssl_resources=1", facebookID]];
 
            // Now add the data to the UI elements
            // ...
        }
    }];
}

Setting the Profile Picture

In the graph request we made above, we also obtained the id field. This is the user's Facebook id. We can use it to create a URL that points to the user's profile picture. To display this picture, we first need to download it. We can download the picture in the same completion handler from above as follows:

// Download the user's facebook profile picture
_imageData = [[NSMutableData alloc] init]; // the data will be loaded in here

// URL should point to https://graph.facebook.com/{facebookId}/picture?type=large&return_ssl_resources=1
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large&return_ssl_resources=1", facebookID]];

NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:pictureURL
                                                          cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                      timeoutInterval:2.0f];
// Run network request asynchronously
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];

To handle an asynchronous url request, we need to add the NSURLConnectionDelegate protocol in the header file. We will use two of the delegate methods. The connection:didReceiveData: method will allow us to build the image file in chunks as it arrives, and the connectionDidFinishLoading: will give us the chance to set the image once it is downloaded.

// Called every time a chunk of the data is received
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [imageData appendData:data]; // Build the image
}

// Called when the entire image is finished downloading
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // Set the image in the header imageView
    headerImageView.image = [UIImage imageWithData:imageData];
}

Logout

Logging out a Facebook PFUser is no different than logging out a regular PFUser. We can statically call logOut on the PFUser object.

- (void)logoutButtonTouchHandler:(id)sender  {
    [PFUser logOut]; // Log out

    // Return to login page
    [self.navigationController popToRootViewControllerAnimated:YES]; 
}

Keeping the User Logged-in

Using the code above, a user would need to log in every time they opened the app. This is because the Facebook session information is not stored between launches. Caching this information can be a confusing ordeal, but Parse makes this very easy by automatically saving the session information in the currentUser object. To keep a user logged in, we need to follow two simple steps. We start by adding the mechanism to bypass the login screen when a cached user is identified and finally ensuring that a user is automatically logged out if the cached session is invalidated (ex. if the password changes).

Bypassing the Login

After a user logs in, Parse will automatically cache the Facebook and Parse sessions in the currentUser object. We simply need to bypass the login screen if a user is currently cached. In our application, we'll perform this check at the end of the viewDidLoad method of LoginViewController. When we find a cached user session, we simply push the next view controller.

- (void)viewDidLoad {
    ...
    if ([PFUser currentUser] && // Check if a user is cached
        [PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]) // Check if user is linked to Facebook
    {
        // Push the next view controller without animation
        [self.navigationController pushViewController:
                                       [[UserDetailsViewController alloc] 
                                       initWithStyle:UITableViewStyleGrouped] 
                                   animated:NO];
    }
}

Handling an Invalidated Session

We cannot know if the cached Facebook session is valid until we attempt to make a request to the API. A session can become invalidated if a user changes their password or revokes the application's privileges. When this happens, the user needs to be logged out. We can identify an invalid session error within a FBRequest completion handler and automatically log the user out.

FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
    if (!error) {
        // handle successful response
    } else if ([error.userInfo[FBErrorParsedJSONResponseKey][@"body"][@"error"][@"type"] isEqualToString:@"OAuthException"]) { // Since the request failed, we can check if it was due to an invalid session
        NSLog(@"The facebook session was invalidated");
        [self logoutButtonTouchHandler:nil];
    } else {
        NSLog(@"Some other error: %@", error);
    }
}];

The application should now be able to log in Parse users through Facebook, display their profile information, log them out and make use of the cached user session. Feel free to download the Xcode project and experiment with the example.