iOS

Sign In with Facebook

Introduction

In the previous guide, we learned how a user signs into a Back4App application using a Google account. As a follow-up, we may now add a sign-in alternative that uses a Facebook account instead. To accomplish this, we first go to Facebook’s developer page and set up the requirements to integrate such functionality in an Xcode project. Facebook provides an SDK to developers to integrate a sign in with Facebook option among different apps.

In this repository, we provide a simple Xcode template where you can test the different sign-in methods we are implementing. This example was already introduced in the log-in guide, you can revisit it for more details about the project.

Prerequisites

To complete this quickstart, you need:

  • A recent version of Xcode.
  • An application created on Back4App.

Goal

To integrate a user sign-in feature using Facebook’s SDK and ParseSwift.

Step 1 - Setting up Facebook’s SDK

  • Once we have the Xcode project linked to the Back4App application, we proceed to add Facebook’s SDK which will allow us to implement the sign-in flow. For this example, we use the Swift Package Manager (SPM) to add Facebook Login dependencies. In the Xcode project, go to File>Add packages… and in the search bar, look for https://github.com/facebook/facebook-ios-sdk.

Once the facebook-ios-sdk appears in the results, click on the Add Package button.

  • Next, go to the apps section of your Facebook developer account and create a new app. Enter the type of app your app belongs to and go to the next page. Enter the remaining information and click on Create app. While you are on your apps page, locate your newly created app, copy its App ID and enter the dashboard by clicking on its name. In the top right, it is located the Client ID associated with your Facebook developer account. These two IDs are necessary for the following configuration.

  • The next configuration Facebook needs to set up the sign-in capability is to enter a couple of key-value data in your Xcode Info.plist file. More precisely, go to your Xcode project navigator, locate the Info.plist and open it as source code. Add the following values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<key>CFBundleURLTypes</key>
<array>
	<dict>
		<key>CFBundleTypeRole</key>
		<string>Editor</string>
		<key>CFBundleURLSchemes</key>
		<array>
			<string>fbAPP_ID</string>
		</array>
	</dict>
</array>
<key>FacebookAppID</key>
<string>APP_ID</string>
<key>FacebookClientToken</key>
<string>CLIENT_TOKEN</string>
<key>LSApplicationQueriesSchemes</key>
<array>
	<string>fbapi</string>
	<string>fb-messenger-share-api</string>
</array>

Replace the APP_ID string with the App ID associated with the newly created app on Facebook. The CLIENT_TOKEN string has to be replaced with the client token located in Dashboard>Settings>Advanced>Security>Client token.

  • Now we have to add the Keychain Sharing capability to the Xcode project. To do this, select your project from the project navigator and go to the targets section. Select a target and go to the Signing & Capabilities tab, then click on the + Capability button and add the Keychain Sharing capability:

  • In the AppDelegate, we add the following line in the application(_:didFinishLaunchingWithOptions:) delegate method (Make sure you import the FacebookCore framework first)
1
2
3
4
5
6
7
8
9
10
11
12
13
import FacebookCore

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    ...     

    ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
    return true
  }
  
  ...
}
  • Lastly, In the SceneDelegate, we add the following code to handle incoming URL contexts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import FacebookCore

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
  ...

  func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else {
      return
    }
        
    ApplicationDelegate.shared.application(
      UIApplication.shared,
      open: url,
      sourceApplication: nil,
      annotation: [UIApplication.OpenURLOptionsKey.annotation]
    )
  }
}

Step 2 - Using Facebook Login with ParseSwift

With FacebookLogin successfully integrated into your Xcode project, we proceed to implement the sign in with Facebook feature. In the project example, the LogInController is in charge of handling and displaying the different sign-in options. We then set up the signInWithFacebookButton action:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// LogInController.swift file
import FacebookLogin
...

class LogInController: UIViewController {
  ...

  private let signInWithFacebookButton: UIButton = {
    let button = UIButton(type: .system)
    button.setImage(UIImage(named: "facebookIcon"), for: .normal)
    button.imageView?.contentMode = .scaleAspectFit
    return button
  }()

  override func viewDidLoad() {
    super.viewDidLoad()
    // ...
    // Layout configuration
    // ...

    signInWithFacebookButton.addTarget(self, action: #selector(handleSignInWithFacebook), for: .touchUpInside)
  }
}

// MARK: - Sign in with Facebook section
extension LogInController {
  @objc fileprivate func handleSignInWithFacebook() {
    // TODO: Here we will implement the sign-in procedure
  }
}

In order to show the sign-in form from Facebook, the FacebookLogin SDK allows us to set up and present it via the signIn(with:presenting:callback) method from the LoginManager class. We have to pass an array containing String values associated with the data we want to gather from Facebook. The common values are public_profile and email.

On the other hand, the second parameter is a callback closure where Facebook returns the user credential (embedded in a LoginManagerLoginResult object) or an error if the authentication fails.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// MARK: - Sign in with Facebook section
extension LogInController {
  @objc fileprivate func handleSignInWithFacebook() {
    let loginManager = LoginManager()
            
    // Method provided by the Facebook SDK. See https://developers.facebook.com/docs/facebook-login/ios/ for more details
    loginManager.logIn(permissions: ["public_profile", "email"], from: self) { [weak self] result, error in
      if let error = error {
        self?.showMessage(title: "Error", message: error.localizedDescription)
        return
      } else if let result = result, result.isCancelled {
        self?.showMessage(title: "Alert", message: "Sign in cancelled")
        return
      }
      
      // Once facebook successfully signs in the user, we retrieve the information related to the sign-in process via the result.token object, an AccessToken class type
      guard let accessToken = result?.token else { fatalError("This dhould never hapen.") }
      
      // TODO: Sign in the user on your Back4App application with the accessToken.
    }
  }
}

We then take this credential and use them for the user to sign in to the Back4App platform. The object representing the user is the following struct (see the Login guide for more details):

1
2
3
4
5
6
7
8
9
10
11
12
import ParseSwift

struct User: ParseUser {
  ...
  
  var username: String?
  var email: String?
  var emailVerified: Bool?
  var password: String?
  
  var age: Int?
}

Therefore, the credential returned by Facebook contains an accessToken and the user’s id that will be used to complete the sign-in process. More precisely, we instantiate a ParseFacebook<User> object and call the login(userId:authenticationToken:completion:) method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// MARK: - Sign in with Facebook section
extension LogInController {
  @objc fileprivate func handleSignInWithFacebook() {
    let loginManager = LoginManager()
            
    // Method provided by the Facebook SDK. See https://developers.facebook.com/docs/facebook-login/ios/ for more details
    loginManager.logIn(permissions: ["public_profile", "email"], from: self) { [weak self] result, error in
      if let error = error {
        self?.showMessage(title: "Error", message: error.localizedDescription)
        return
      } else if let result = result, result.isCancelled {
        self?.showMessage(title: "Alert", message: "Sign in cancelled")
        return
      }
            
      // Once facebook successfully signed in the user, we retrieve the information related to the sign in process via the result.token object, an AccessToken class type
      guard let accessToken = result?.token else { fatalError("This dhould never hapen.") }
      
      // With the accessToken returned by Facebook, you need to sign in the user on your Back4App application
      User.facebook.login(userId: accessToken.userID, accessToken: accessToken.tokenString) { [weak self] result in
        // Returns the User object asociated to the facebook user returned by Facebook
        switch result {
        case .success(let user):
          // After the login succeeded, we send the user to the home screen
          // Additionally, you can complete the user information with the data provided by Facebook
          let homeController = HomeController()
          homeController.user = user

          self?.navigationController?.pushViewController(homeController, animated: true)
        case .failure(let error):
          // Handle the error if the login process failed
          self?.showMessage(title: "Failed to sign in", message: error.message)
        }
      }
    }
  }
}

Step 3 - Verifying user sign in and session creation

To make sure that the Google sign-in worked, you can look at your Back4App application dashboard and see the new User containing the Facebook authData parameters.

Back4App

You can also verify that a valid session was created in the dashboard, containing a pointer to the corresponding User object.

Back4App

Step 4 - Linking an existing User to a Facebook account

In case your iOS App requires you to associate a Facebook account to an existing user in your Back4App platform, the ParseFacebook<User> object implements the method link(id:accessToken:completion:) where you pass the userId of the Facebook account and the accessToken associated with the session

1
2
3
4
5
6
7
8
9
10
11
let facebookUserId: String // The userID of the Facebook account to link to
let accessToken: String = AccessToken.current!.tokenString // The access token of the currently signed in Facebook user

User.facebook.link(userId: facebookUserId, accessToken: accessToken) { result in
  switch result {
  case .success(let user):
    // Linking succeeded, the user is now linked to the corresponding Facebook account
  case .failure(let error):
    // Linking failed, handle the error
  }
}

Step 5 - Signing out

The sign-out process does not vary from the standard way of calling the User.signout() method (detailed in previous guides). However, when a user signs in with a Facebook account, for consistency, you have to sign out the current Facebook user as well. We can accomplish this by calling the following method alongside User.signout().

1
LoginManager().logOut()

In order to verify if the current user has a linked Facebook account, you can check it by looking at the User.current?.authData dictionary.

Step 6 - Run the app

You can go to this repository and download the example project. Before running the project, make sure you set up the provisioning profiles with the ones associated with your developer account.

Conclusion

At the end of this guide, you learned how to sign in or link existing Back4App users on iOS using sign in with Facebook. In the next guide, we will continue with a different sign-in method.