Touch ID Authentication using Swift 3

In iOS7, iPhone 5s, Apple introduced touch ID. Touch ID is a way to authenticate users using finger print. In this post, I will show you how to implement this using swift 3.

Let’s create a project and call it TouchIDAuthentication. Click on the project, go to General. Scroll to the bottom, you will a section called Linked Frameworks and Libraries. Click on + sign for the section.

Alt Text

Search for LocalAuthentication. Select the framework and click on add.

Alt Text
Go to viewController.swift, import LocalAuthentication. Create a method to authenticate users using touchID.

func touchIDAuthentication() {
    let context = LAContext() //1

    //2
    if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &errorPointer) {
        //3
        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason, reply: { (success, error) in
            if success {
                DispatchQueue.main.async {
                    //Authentication was successful
                }

            }else {
                DispatchQueue.main.async {
                    //Authentication failed. Show alert indicating what error occurred
                    self.displayErrorMessage(error: error as! LAError )
                }

            }
        })
    }else {
       //Touch ID is not available on Device, use password.
        self.showAlertWith(title: "Error", message: (errorPointer?.localizedDescription)!)

    }
}

First we get the device current authentication context. An LAContext object represents an authentication context. The LAContext class provides a programmatic interface for evaluating authentication policies and access controls, managing credentials, and invalidating authentication contexts.

Next we check to see if the device can handle touch ID authentication by calling the method
func canEvaluatePolicy(_ policy: LAPolicy, error: NSErrorPointer) -> Bool
this method takes a policy and error pointer and returns a boolean that is true if the device has touch ID authentication capability, otherwise it returns false.

LAPolicy: Policies specifying which forms of authentication are acceptable. It has two cases:
deviceOwnerAuthenticationWithBiometrics:Indicates that the device owner authenticated using Touch ID.

deviceOwnerAuthentication:Indicates that the device owner authenticated using Touch ID or the device password.

We want to check for Touch ID only, so we use the policy deviceOwnerAuthenticationWithBiometrics.

canEvaluatePolicy method takes a second parameter which is an NSErrorPointer. This is simply a pointer to an error object that can tell us what happened if an error occurs.

Create a global variable.
var errorPointer:NSError?

We pass this as the second parameter required by the method.

If canEvaluatePolicy method returns false, Touch ID is not available. We will displaying alert telling the user Touch ID is not available.
If canEvaluatePolicy method returns returns true, we go ahead and try to authenticate the user with Touch ID. We do this by calling the method:
func evaluatePolicy(_ policy: LAPolicy, localizedReason: String, reply: @escaping (Bool, Error?) -> Void)

This method asynchronously evaluates an authentication policy. The reply block is used to deliver the results of the evaluation. Evaluating a policy may involve prompting the user for various kinds of interaction or authentication. The actual behavior is dependent on the evaluated policy and device type. It can also be affected by installed configuration profiles.

The method takes a policy, which we pass deviceOwnerAuthenticationWithBiometrics, a string which tells the user why we want to authenticate. It should be a localized string as we want it to be shown to user in user’s local language.

Let’s create another global variable
let reason = NSLocalizedString("Just because we can", comment: "authReason”) and pass the variable for the argument localizedReason.

Next we open up the completion block which returns a boolean and an optional Error?. we call the boolean success and the optional Error error.
If success is equal to true then authentication was successful. We can go ahead and update UI here in the main queue.
Otherwise we display alert indicating the error that occurred.

func displayErrorMessage(error:LAError) {
    var message = ""
    switch error.code {
    case LAError.authenticationFailed:
        message = "Authentication was not successful because the user failed to provide valid credentials."
        break
    case LAError.userCancel:
        message = "Authentication was canceled by the user"
        break
    case LAError.userFallback:
        message = "Authentication was canceled because the user tapped the fallback button"
        break
    case LAError.touchIDNotEnrolled:
        message = "Authentication could not start because Touch ID has no enrolled fingers."
    case LAError.passcodeNotSet:
        message = "Passcode is not set on the device."
        break
    case LAError.systemCancel:
        message = "Authentication was canceled by system"
        break
    default:
        message = error.localizedDescription
    }

    self.showAlertWith(title: "Authentication Failed", message: message)
}

What is most important to us here is the switch statement. LocalAuthentication framework comes with LAError Struct. We can switch on the Code (enum LAError.Code) which has different cases and the message displayed here tells you what each error code mean.

According to apple doc :

case authenticationFailed: Authentication was not successful because the user failed to provide valid credentials.

case userCancel: Authentication was canceled because the user tapped the cancel button in the authentication dialog.

case userFallback: Authentication was canceled because the user tapped the fallback button in the authentication dialog, but no fallback is available for the authentication policy.

case systemCancel: Authentication was canceled by system—for example, if another application came to foreground while the authentication dialog was up.

case passcodeNotSet: Authentication could not start because the passcode is not set on the device.

case touchIDNotAvailable: Authentication could not start because Touch ID is not available on the device.

case touchIDNotEnrolled: Authentication could not start because Touch ID has no enrolled fingers.

Let's quickly add extension to UIViewController to have quick way of creating an alert. This is not really necessary for a small app such as this, but I’m used to doing it this way :) . Also we will need this when we display alert when canEvaluatePolicy fails.

extension UIViewController {
func showAlertWith(title:String, message:String) {
    let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
    let actionButton = UIAlertAction(title: "OK", style: .default, handler: nil)
    alertController.addAction(actionButton)
    self.present(alertController, animated: true, completion: nil)
}

}

We already called this method in ‘else’ statement for ‘if success’ statement.

Override viewDidAppear and call the function touchIDAuthentication

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    touchIDAuthentication()
}

Before we run the app, add this
self.showAlertWith(title: "Error", message: (errorPointer?.localizedDescription)!)

to the else statement for the if canEvaluatePolicy statement.

Run the app on simulator, you get this.

Alt Text

of course simulator doesn't support Touch ID. Run it on device, play around with and see it work!
SWEET!!!
complete code can be found here