Touch ID or Face ID Authentication in Swift

In this short tutorial, you will learn how to add Biometric Authentication to your iOS app built with Swift.

iOS devices starting from iPhone 5S until iPhone 8 Plus will use Touch ID for biometric authentication, while devices starting from iPhone X and above will use Face ID for biometric authentication.

Update Info.plist File

To be able to use Face ID in your app you will need to update an Info.plist file with a new property called: Privacy – Face ID Usage Description. To do so, you can either open the Info.plist file as source code and add the following:

<key>NSFaceIDUsageDescription</key>
<string>To access user profile related information you must authenticate</string>

Or you can open Info.plist as a property list and add a new entry Privacy – Face ID Usage Description with a description that makes sense. Although the string should clearly explain why your app needs access to this authentication mechanism.

Privacy Face ID Usage Description

 

Create Local Authentication Context

To be able to perform biometric authentication in your app, you will need to use the Local Authentication framework. In your Swift code, first import the LocalAuthentication framework like it is shown in the code snippet below:

import LocalAuthentication

Once you have the LocalAuthentication framework imported, you will be able to create and configure the LAContext object. For example:

let localAuthenticationContext = LAContext()
localAuthenticationContext.localizedFallbackTitle = "Please use your Passcode"

Preflight Check if Biometrics is Available

To check if biometrics is available on the device your app is runnig use the following function canEvaluatePolicy(_:error:).   

Biometrics or Passcode

If user cannot use Touch ID or Face ID it is possible to make them input their Passcode instead. To check if this option is available on device use the canEvaluatePolicy() method together with LAPolicy.deviceOwnerAuthentication parameter. For example:

let localAuthenticationContext = LAContext()
localAuthenticationContext.localizedFallbackTitle = "Please use your Passcode"

var authorizationError: NSError?

if localAuthenticationContext.canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: &authorizationError) {
 
    print("Biometrics is supported. User can use Passcode option if needed.")

}

Check if Biometrics Only is Available 

If user should use Touch ID or Face ID only and should not be able to use Password, use the LAPolicy.deviceOwnerAuthenticationWithBiometrics when evaluating local authentication policy. For example:

let localAuthenticationContext = LAContext() 

var authorizationError: NSError? 

if localAuthenticationContext.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &authorizationError) { 

    print("Biometrics is supported. If needed, user can be prevented from using Passcode")

}

Determine Biometry Type: Touch ID or Face ID

It is helpful to know which exactly biometry type is available on user’s device. Knowing the biometry type can help us present user with the right user interface, images and text related to a specific biometric authentication. To determine if user’s device supports Touch ID or Face ID you can use the LAContext and LABiometryType in the following way:

let biometricType = localAuthenticationContext.biometryType == LABiometryType.faceID ? "Face ID" : "Touch ID"
print("Supported Biometric type is: \( biometricType )")

Present Biometric Authentication to User

If biometric authentication is available on user’s device you can request user authenticate with either Touch ID or Face ID depending on the supported Biometric Type. To prompt user with biometric authentication you will use the evaluatePolicy(_:localizedReason:reply:) methodFor example:

Prompt with option to use Passcode instead: 

localAuthenticationContext.evaluatePolicy(LAPolicy.deviceOwnerAuthentication, localizedReason: reason) { (success, evaluationError) in
                if success {
                    print("Success")
                } else {
                    print("Error \(evaluationError!)")
                }
            }

Prompt with no option to use Passcode:

localAuthenticationContext.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { (success, evaluationError) in
    if success {
        print("Success")
    } else {
        print("Error \(evaluationError!)")
    }
}

Error Description

If biometric authentication fails, it is helpful to know the reason why. The evaluatePolicy() method can provide us with an error object which we can use to determine the reason of failure. Below is an example of how you can do it:

func getErrorDescription(errorCode: Int) -> String {
 
    switch errorCode {
        
    case LAError.authenticationFailed.rawValue:
        return "Authentication was not successful, because user failed to provide valid credentials."
        
    case LAError.appCancel.rawValue:
        return "Authentication was canceled by application (e.g. invalidate was called while authentication was in progress)."
        
    case LAError.invalidContext.rawValue:
        return "LAContext passed to this call has been previously invalidated."
        
    case LAError.notInteractive.rawValue:
        return "Authentication failed, because it would require showing UI which has been forbidden by using interactionNotAllowed property."
        
    case LAError.passcodeNotSet.rawValue:
        return "Authentication could not start, because passcode is not set on the device."
        
    case LAError.systemCancel.rawValue:
        return "Authentication was canceled by system (e.g. another application went to foreground)."
        
    case LAError.userCancel.rawValue:
        return "Authentication was canceled by user (e.g. tapped Cancel button)."
        
    case LAError.userFallback.rawValue:
        return "Authentication was canceled, because the user tapped the fallback button (Enter Password)."
        
    default:
        return "Error code \(errorCode) not found"
    }
    
 }

Putting It All Together

To put the above code snippets together to an example which checks if biometric authentication is available and if it is, request user to authenticate, you can follow the below code snippet:

let localAuthenticationContext = LAContext()
localAuthenticationContext.localizedFallbackTitle = "Please use your Passcode"

var authorizationError: NSError?
let reason = "Authentication is required for you to continue"

if localAuthenticationContext.canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: &authorizationError) {
    
    let biometricType = localAuthenticationContext.biometryType == LABiometryType.faceID ? "Face ID" : "Touch ID"
    print("Supported Biometric type is: \( biometricType )")
    
    localAuthenticationContext.evaluatePolicy(LAPolicy.deviceOwnerAuthentication, localizedReason: reason) { (success, evaluationError) in
        if success {
            print("Success")
        } else {
            print("Error \(evaluationError!)")
            if let errorObj = evaluationError {
                let messageToDisplay = self.getErrorDescription(errorCode: errorObj._code)
                print(messageToDisplay)
            }
        }
    }
      
} else {
    print("User has not enrolled into using Biometrics")
}


Leave a Reply

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