Login with Facebook & Twitter

Login with Facebook & Twitter

Facebook recently released SDK 3.0 for iOS. Although it is still in beta it offers some huge improvements in comparison to the last SDK. One of the most significant changes is the simplified user session management.
This blog post will sum up the most significant steps in developing an iOS app that uses Facebook's and Twitter's authentication functionality.
The source code of the full app can be found at: http://subsites.icapps.be/blog/Socials.zip

This project requires a Facebook app id. Please follow the first step of Facebook integration described in this post to get things up and running.

Twitter integration

1. As of iOS 5.0 Twitter is integrated in the OS. To use this built-in functionality two additional frameworks need to be added to your XCode project: Accounts.framework and Twitter.framework

2. Next define a public method in your AppDelegate header file.

- (void)getTwitterAccountOnCompletion:(void(^)(ACAccount *))completionHandler;

Implement the method:

#pragma mark - Twitter SDK
- (void)getTwitterAccountOnCompletion:(void (^)(ACAccount *))completionHandler {
    ACAccountStore *store = [[ACAccountStore alloc] init];
    ACAccountType *twitterType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
    [store requestAccessToAccountsWithType:twitterType withCompletionHandler:^(BOOL granted, NSError *error) {
        if(granted) {
            // Remember that twitterType was instantiated above
            NSArray *twitterAccounts = [store accountsWithAccountType:twitterType];

            // If there are no accounts, we need to pop up an alert
            if(twitterAccounts == nil || [twitterAccounts count] == 0) {
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No Twitter Accounts"
                                                                message:@"There are no Twitter accounts configured. You can add or create a Twitter account in Settings."
                                                               delegate:nil
                                                      cancelButtonTitle:@"OK"
                                                      otherButtonTitles:nil];
                [alert show];
            } else {
                //Get the first account in the array
                ACAccount *twitterAccount = [twitterAccounts objectAtIndex:0];
                //Save the used SocialAccountType so it can be retrieved the next time the app is started.
                [[NSUserDefaults standardUserDefaults] setInteger:SocialAccountTypeTwitter forKey:kSocialAccountTypeKey];
                [[NSUserDefaults standardUserDefaults] synchronize];
                //Call the completion handler so the calling object can retrieve the twitter account.
                completionHandler(twitterAccount);
            }
        }
    }];
}

This piece of code will retrieve the first Twitter account stored on the iOS device. The first time this code will run, the OS will ask the user for his permission to access this information.
When the Twitter account is retrieved, the completion handler will be called passing on the Twitter account.
The SocialAccountTypeTwitter is one of the values of an enum defined as follows:

	typedef enum SocialAccountType  {
                                    SocialAccountTypeFacebook = 1,
                                    SocialAccountTypeTwitter = 2
                                } SocialAccountType;

Don’t forget the imports: "<Accounts/Accounts.h>" and "<Twitter/Twitter.h>”!

3. Create a new UIViewController, “LoginViewController” that will present the user with two login buttons.

The IBAction behind the Twitter button should be implemented like this:

- (IBAction)twitterButtonPressed:(UIButton *)sender {    
    //Get a reference to the application delegate.
    AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;

    //Get Twitter account, stored in on the device, for the first time.
    [appDelegate getTwitterAccountOnCompletion:^(ACAccount *twitterAccount){
        //If we successfully retrieved a Twitter account
        if(twitterAccount) {
            //Make sure anything UI related happens on the main queue
            dispatch_async(dispatch_get_main_queue(), ^{
                HomeViewController *homeViewController = [[HomeViewController alloc] initWithSocialAccountType:SocialAccountTypeTwitter];
                [self.navigationController pushViewController:homeViewController animated:YES];
            });
        }
    }];
}

The only thing happening here is retrieving the Twitter account, checking if one was returned and going to the next UIViewController that will load some data from the Twitter user. This next UIViewController, HomeViewController will be briefly discussed at the end of this post.

Facebook integration

1. The first step is to install the Facebook SDK. It can be downloaded at: https://developers.facebook.com/ios/

2. After installing the SDK follow the “Getting started” guide to prepare your app for Facebook authentication. The guide can be found at: https://developers.facebook.com/docs/getting-started/getting-started-with-the-ios-sdk/

3. With everything in place we switch back to the implementation file of AppDelegate and add this method to the implementation:

#pragma mark - URL handler
- (BOOL)application:(UIApplication *)application 
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication 
         annotation:(id)annotation 
{
    return [FBSession.activeSession handleOpenURL:url]; 
}

The most common way of authenticating using Facebook is through the Facebook app for iOS. When your app tries to authenticate the user using Facebook, it will redirect the user to the Facbook app and request the user to grant your app access to certain information. After the user has granted this access, he will be redirected to your app. This method will be called after this redirect and will ensure your Facebook session will be initialized correctly.

4. Next implement the three following methods in AppDelegate:

#pragma mark - Facebook SDK
- (void)openFacebookSession {
    NSArray *facebookPermissions = [NSArray arrayWithObjects:@"user_about_me", @"user_status", nil];
    [FBSession sessionOpenWithPermissions:facebookPermissions completionHandler:
     ^(FBSession *session, FBSessionState state, NSError *error) {
         [self sessionStateChanged:session state:state error:error];
     }];
}

- (void)closeFacebookSession {
    [FBSession.activeSession closeAndClearTokenInformation];
}

- (void)sessionStateChanged:(FBSession *)session 
                      state:(FBSessionState) state
                      error:(NSError *)error {
    switch (state) {
        case FBSessionStateOpen: {
            //Save the used SocialAccountType so it can be retrieved the next time the app is started.
            [[NSUserDefaults standardUserDefaults] setInteger:SocialAccountTypeFacebook forKey:kSocialAccountTypeKey];
            [[NSUserDefaults standardUserDefaults] synchronize];
            //Go to the HomeViewController
            HomeViewController *homeViewController = [[HomeViewController alloc] initWithSocialAccountType:SocialAccountTypeFacebook];
            [self.navigationController pushViewController:homeViewController animated:YES];
        }
            break;
        case FBSessionStateClosed:
        case FBSessionStateClosedLoginFailed:
            // Once the user has logged out, we want them to be looking at the root view.
            [self.navigationController popToRootViewControllerAnimated:YES];            
            [FBSession.activeSession closeAndClearTokenInformation];
            break;
        default:
            break;
    }

    if (error) {
        UIAlertView *alertView = [[UIAlertView alloc]
                                  initWithTitle:@"Facebook Error"
                                  message:error.localizedDescription
                                  delegate:nil
                                  cancelButtonTitle:@"OK"
                                  otherButtonTitles:nil];
        [alertView show];
    }    
}

Make sure the “openFacebookSession” and the “closeFacebookSession” are public methods. These methods will be called when the user wants to login or logout.
The “sessionStateChanged” method is called in the completion handler of “sessionOpenWithPermissions” and will ensure the right event will be triggered whenever the status of the active Facebook session changes.
Notice the case “FBSessionStateOpen:”. Here the next UIViewController will be initialized with “SocialAccountTypeFacebook” and pushed on the navigation stack.

5. “openFacebookSession” needs to be called at some point. This should happen after the user pressed the Facebook button on the “LoginViewController”. This is the IBAction linked to that button:

- (IBAction)facebookButtonPressed:(UIButton *)sender {
    AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate openFacebookSession];
}

The "HomeViewController" will retrieve some basic information from the users profile. I will not elaborate on this. For more information please refer to the source code.

The basic setup of authentication for these popular social networks, will enable your apps users to signup for your service with a single click. After they grant your app access, you can retrieve the information you need, like email address, full name, birthday, …

To learn more about retrieving information from these social networks you can download the source code of this project.
Additionally the following resources can be very helpful:

Facebook:
https://developers.facebook.com/docs/sdk-reference/iossdk/3.0/
https://developers.facebook.com/docs/opengraph/

Twitter:
https://dev.twitter.com/docs/ios