iOS - Face ID biometric integration

In any project that uses biometrics, include the NSFaceIDUsageDescription key in your app’s Info.plist file. Without this key, the system won’t allow your app to use Face ID.

let authContext = LAContext()
        authContext.localizedFallbackTitle = "Use Passcode"
        authContext.localizedCancelTitle = "Cancel"

var authError: NSError?

if authContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: &authError) {
   evaluatePolicy(policy, context: authContext)
} else {
     guard let error = authError else { return }
     print("Error: \(error.code)")
     checkError(error)
}

private func evaluatePolicy(_ policy: LAPolicy, context: LAContext) {
        context.evaluatePolicy(policy, localizedReason: reason) { (success, error) in
            if success {
                print("Success")

            } else {
                guard let error = error else { return }
                self.checkError(error as NSError)
            }
        }
}

private func checkError(_ error: NSError) {
    guard let error = error as? LAError else { return }

    switch error.code {
    case .authenticationFailed:
        print("authenticationFailed")
        requestAuth(policy: .deviceOwnerAuthentication)
    case .userFallback:
        print("userFallback")
        requestAuth(policy: .deviceOwnerAuthentication)
    case .userCancel:
        print("userCancel")
    case .systemCancel:
        print("systemCancel")
    case .passcodeNotSet:
        print("passcodeNotSet")
    case .appCancel:
        print("appCancel")
    case .invalidContext:
        print("invalidContext")
    case .notInteractive:
        print("notInteractive")
    default:
        checkBioMetricError(error)
    }
}

private func checkBioMetricError(_ error: LAError) {
    if #available(iOS 11.0, *) {

        switch error.code {
        case .biometryNotAvailable,
             .biometryNotEnrolled,
             .biometryLockout:
            requestAuth(policy: .deviceOwnerAuthentication)

        default: break
        }
    } else {
        switch error.code {

        case .touchIDNotAvailable,
             .touchIDNotEnrolled,
             .touchIDLockout:
            requestAuth(policy: .deviceOwnerAuthentication)

        default: break
        }
    }
}

That is only happen in simulator, in actual device the canvas is occupied by face icon animation.

The localizedReason is for only for Touch ID, since they both sharing the same API.

Update 1: Added screen recordings:

  • iPhone X: https://youtu.be/lklRnLNHyQk
  • iPhone 7: https://youtu.be/iIcduvD5JO0
  • iPhone X Simulator: https://youtu.be/bOlRVLIND5c

They all ran the same code:

func beginFaceID() {

    guard #available(iOS 8.0, *) else {
        return print("Not supported")
    }

    let context = LAContext()
    var error: NSError?

    guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
        return print(error)
    }

    let reason = "Face ID authentication"
    context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { isAuthorized, error in
        guard isAuthorized == true else {
            return print(error)
        }

        print("success")
    }

}

Here is Working code for both TouchID & FaceID with all Error Codes (Swift 4)

https://stackoverflow.com/a/52093551/10150796