In WWDC19, Apple unveiled “Sign In with Apple”,  a new way for users to create accounts and sign in into iOS apps. The benefits for using this is that you are getting a two-factor authenticated, secure login system for free. Additionally, it reduces friction in the on boarding flow, which is great in terms of conversion and growth. In this iOS tutorial, we are going to learn how to implement the Sign in with Apple button in Swift.

Sign In with Apple

By the way, Apple is making this mandatory if your app uses other third party login services (such as Facebook, Google, or Twitter Sign In). So in that case, you really don’t have any other alternative, but to implement the Sign in with Apple button. Fortunately, setting up Apple authentication is quite simple. Let’s assume that you have already created a login screen – we will start by adding the Apple branded button.

Before we go any further, make sure you do the following things first:

1. Add “Sign in With Apple” Capability in Xcode.

apple signin capability

2. Add a New Key to Your Apple’s Developer Account

Head over to your Apple developer account -> Certificates, Identifiers and Profiles -> Key and add a new key – name it what ever you want to, check “Sign In With Apple” and press Configure.

apple signin

Then link that Key to your project’s bundle identifier.

3. Implement Sign in with Apple button in Swift

First, let’s import AuthenticationServices, which is an Apple framework handling authentication.

import AuthenticationServices

Then, create a button using the ASAuthorizationAppleIDButton button:

var appleLogInButton : ASAuthorizationAppleIDButton = {
    let button = ASAuthorizationAppleIDButton()
    button.addTarget(self, action: #selector(handleLogInWithAppleID), for: .touchUpInside)
    return button
}()

apple sign in button

We have given the button a target function – which will be called when the button is tapped. Let’s call this ‘handleLogInWithAppleID‘.

Now inside this function is where we will be requesting the user for Apple ID verification:

@objc func handleLogInWithAppleID() {
     let request = ASAuthorizationAppleIDProvider().createRequest()
     request.requestedScopes = [.fullName, .email]
     
     let controller = ASAuthorizationController(authorizationRequests: [request])
     
     controller.delegate = self
     controller.presentationContextProvider = self
     
     controller.performRequests()
 }

We are first creating a request, and in requestScopes asking the user for a .fullName and .email. Basically this is what we would like to get from user’s Apple ID profile. This is completely optional – and only grab this data if you really need to.

With that done, we will initialize the ASAuthorizationController by passing in our request. In order to get the result of the verification we will have to access the delegate functions, via the ASAuthorizationControllerDelegate protocol.

Let’s start with the first delegate protocol, which is

extension ViewController: ASAuthorizationControllerDelegate

and conform to these two delegate functions:

extension ViewController: ASAuthorizationControllerDelegate {
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        switch authorization.credential {
        case let appleIDCredential as ASAuthorizationAppleIDCredential:
            let userIdentifier = appleIDCredential.user
        
            let defaults = UserDefaults.standard
            defaults.set(userIdentifier, forKey: "userIdentifier1")
            
            //Save the UserIdentifier somewhere in your server/database
            let vc = UserViewController()
            vc.userID = userIdentifier
            self.present(UINavigationController(rootViewController: vc), animated: true)
            break
        default:
            break
        }
    }

Inside authorizationController didCompleteWithAuthorizationwe are getting the user credentials after the user has successfully signed in. Check if the credential is ASAuthorizationAppleIDCredential, and grab the properties from the returned user credentials.

For purposes of this demo, we are saving this information in UserDefaults. This is not the safest way to go, and using Keychain or maybe your own secure server should be the way to go.

Once the userIdentifier is saved, we can present a new view controller – detailing user’s info (in this case User’s ID) or what ever you like. Make sure to handle the didCompleteWithError  method as well with maybe an alert.

The second protocol that we need to conform to is the one in which we are telling the app to show the authorization view on a particular window.

extension ViewController: ASAuthorizationControllerPresentationContextProviding {
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
           return self.view.window!
    }
}

 

Now this is basically it, the user has created a new account, you have access to user’s email, name, and other requested information.

Now let’s talk about getting the state of the user credentials. Are these credentials still authorized, or have they been revoked?

This could be done in AppDelegate or SceneDelegate, depending if you are using Storyboards or programmatically displaying the rootViewController. Upon the launch of the app, we will check if the user is logged in or not and redirect the user to the appropriate initial view controller.

Let’s use the userIdentifier that we saved in UserDefault to grab the credential state:

if let userIdentifier = UserDefaults.standard.object(forKey: "userIdentifier1") as? String {
                  let authorizationProvider = ASAuthorizationAppleIDProvider()
                  authorizationProvider.getCredentialState(forUserID: userIdentifier) { (state, error) in
                      switch (state) {
                      case .authorized:
                          print("Account Found - Signed In")
                          DispatchQueue.main.async {
                               self.showUserViewController(scene: windowScene)
                          }
                          break
                      case .revoked:
                          print("No Account Found")
                          fallthrough
                      case .notFound:
                           print("No Account Found")
                           DispatchQueue.main.async {
                                self.showLogInViewController(scene: windowScene)
                           }
                          
                       
                      default:
                          break
                      }
                  }
           }

So essentially we are first grabbing the userIdentifier string from UserDefaults, and then using the getCredentialState(for UserID: _ )  method to get the state. We get the response with two parameters – in this case we have named one ‘state‘, the other ‘error‘.

We will go through the state, and see if it is authorized, revoked or notFound. If it is authorized, we will show the UserViewController, otherwise we will show the LogInViewController and make the user log in again.

So the first time you launch the app, you will be taken to the log in view controller, where when you tap on ‘Sign In With Apple‘ button, you will create a new account with your Apple ID and a new view controller will be presented. If the credentials are not revoked, the next time you launch the app you will be taken to the UserViewController.

Conclusion

All iOS apps that used third party log in services (such as Facebook Login, Google or Twitter Sign in, etc) have to also add a Sign in with Apple button on their onboarding screens. You can read Apple’s guidelines on what are the product rules around this.

Fortunately, implementing the Sign in with Apple feature in any iOS Swift app is extremely straightforward, due to the AuthenticationServices framework, which does the heavy lifting for us. With one special UIButton and a couple of delegate methods, any iOS developer can implement the Sign in With Apple feature in their Swift apps. Happy Coding!

 


Leave a Reply

Your email address will not be published. Required fields are marked *