How to programmatically check support of 'Face Id' and 'Touch Id'

49,693

Solution 1

With Xcode 9, Look at LocalAuthentication -> LAContext -> LABiometryType.

LABiometryType is a enum with values as in attached image

enter image description here

You can check which authentication type supported by device between Touch ID and FaceID or none.

Edit:

Apple have updated values for this enum LABiometryType. none is deprecated now.

enter image description here

Extension to check supported Biometric Type with Swift 5:

import LocalAuthentication

extension LAContext {
    enum BiometricType: String {
        case none
        case touchID
        case faceID
    }

    var biometricType: BiometricType {
        var error: NSError?

        guard self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            return .none
        }

        if #available(iOS 11.0, *) {
            switch self.biometryType {
            case .none:
                return .none
            case .touchID:
                return .touchID
            case .faceID:
                return .faceID
            @unknown default:
                #warning("Handle new Biometric type") 
            }
        }
        
        return  self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
    }
}

Solution 2

I've been struggling to get this to work and found that I needed to use a single instance of the LAContext and needed to call the LAContextInstance.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) before getting the biometryType. Here is my final code with support for older iOS versions:

import LocalAuthentication

static func biometricType() -> BiometricType {
    let authContext = LAContext()
    if #available(iOS 11, *) {
        let _ = authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
        switch(authContext.biometryType) {
        case .none:
            return .none
        case .touchID:
            return .touch
        case .faceID:
            return .face
        }
    } else {
        return authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touch : .none
    }
}

enum BiometricType {
    case none
    case touch
    case face
}

Solution 3

As I am a big fan of extension. I phrase this answer a little differently. Essense is the same. This is a drop-in extension.

import LocalAuthentication

extension LAContext {
    enum BiometricType: String {
        case none
        case touchID
        case faceID
    }

    var biometricType: BiometricType {
        var error: NSError?

        guard self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            // Capture these recoverable error thru Crashlytics
            return .none
        }

        if #available(iOS 11.0, *) {
            switch self.biometryType {
            case .none:
                return .none
            case .touchID:
                return .touchID
            case .faceID:
                return .faceID
            }
        } else {
            return  self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
        }
    }
}

Use like this:

var currentType = LAContext().biometricType

Solution 4

I made a singleton class for local authentication as it helps to initialise an instance one time using static property for the entire application.

import Foundation
import LocalAuthentication

public class LocalAuthManager: NSObject {

    public static let shared = LocalAuthManager()
    private let context = LAContext()
    private let reason = "Your Request Message"
    private var error: NSError?

    enum BiometricType: String {
        case none
        case touchID
        case faceID
    }

    private override init() {

    }

    // check type of local authentication device currently support
    var biometricType: BiometricType {
        guard self.context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            return .none
        }

        if #available(iOS 11.0, *) {
            switch context.biometryType {
            case .none:
                return .none
            case .touchID:
                return .touchID
            case .faceID:
                return .faceID
            }
        } else {
            return self.context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) ? .touchID : .none
        }
    }
}

Implementation:

func checkAuth() {
     let authType = LocalAuthManager.shared.biometricType
        switch authType {
        case .none:
            print("Device not registered with TouchID/FaceID")
        case .touchID:
            print("Device support TouchID")
        case .faceID:
            print("Device support FaceID")
        }
 }

Solution 5

Face ID is available from iOS 11 and iPhone X comes with iOS 11 by default. In the LocalAuth framework they have added a 'biometryType' property which can give you ability to detect whether Face ID is available on device.

/// checks if face id is avaiable on device
func faceIDAvailable() -> Bool {
    if #available(iOS 11.0, *) {
        let context = LAContext()
        return (context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: nil) && context.biometryType == .faceID)
    }
    return false
}
Share:
49,693
Krunal
Author by

Krunal

Contact me, if you're looking for a CTO or Engineering Director/VP. contact: +91 - 9737007007 mail: [email protected] Following some great SO users....

Updated on October 14, 2021

Comments

  • Krunal
    Krunal over 2 years

    I've integrated LocalAuthentication for my app security purpose, which has been supporting 'Touch Id' based supporting. But now, apple has recently added 'Face Id' based authentication also.

    How can I check, which type of authentication is supported by a device. Touch Id or Face Id?

  • tfrank377
    tfrank377 over 6 years
    In the just released Xcode 9.2 beta, with iOS 11.2, the LABiometryType enum values have changed to faceID and touchID.
  • Surjeet Singh
    Surjeet Singh over 6 years
    @tfrank377 Thanks for reminding me. I have updated answer.
  • Sethmr
    Sethmr over 6 years
    Thanks for this. Exactly what I was looking for :)
  • Valeriy Van
    Valeriy Van over 6 years
    Documentation for biometryType states: "This property is set only when canEvaluatePolicy succeeds for a biometric policy". So if canEvaluatePolicy fails (e.g. touchid/faceid is deactivated at all or per app) biometricType() returns .none. So biometricType() checking not availability of hardware but if hardware can be accessed by the app.
  • SAHM
    SAHM over 6 years
    @ValeriyVan Have you figured out a way to check availability of hardware on device? It seems like everyone is giving biometricType as the answer, but, like you said, it is actually the wrong answer if you are just trying to present the user a button that says either "Face ID" or "Touch ID" so that the user can authorize one or the other when device cannot yet be authenticated by biometrics.
  • Valeriy Van
    Valeriy Van over 6 years
    @SAHM, actually I failed to find better way then checking device type. That is bad way as it is not future proof. Hope Apple will update API to address this issue.
  • SAHM
    SAHM over 6 years
    It's funny because they actually said "Don't reference Touch ID on a device that supports Face ID. Conversely, don't reference Face ID on a device that supports Touch ID. Check the device's capabilities and use the appropriate terminology. For developer guidance, see LABiometryType." But there is no way to do just that unless the user has already authorized it. developer.apple.com/ios/human-interface-guidelines/…
  • Sazzad Hissain Khan
    Sazzad Hissain Khan about 6 years
    you must import LocalAuthentication
  • shaqir saiyed
    shaqir saiyed over 5 years
    can we detect whether device supports Face ID or touch ID using only simulator ?
  • shaqir saiyed
    shaqir saiyed over 5 years
    can we detect whether device supports Face ID or touch ID using only simulator ?
  • shaqir saiyed
    shaqir saiyed over 5 years
    can we detect whether device supports Face ID or touch ID using only simulator ?
  • Surjeet Singh
    Surjeet Singh over 5 years
    Yes, You can test this using simulator. Select Simulator -> Hardware -> Touch ID -> Cases This will provide support for both based on simulator.
  • Usman Awan
    Usman Awan over 5 years
    @shaqirsaiyed Yes. We can detect whether device supports Face ID or Touch ID. When you run the app on iPhoneX or later devices it will automatically detects the Face ID else for iPhone 8 or iPhone 8Plus devices it will detect Touch ID.
  • swift2geek
    swift2geek over 5 years
    you have not answered the question. the question not about what it is? but how to/>
  • swift2geek
    swift2geek over 5 years
    this question is the right one and should be chosen like accepted.
  • Surjeet Singh
    Surjeet Singh over 5 years
    @swift2geek Krunal have already implemented LocalAuthentication, and only confused how he can check that device supports FaceId or TouchID. For that I guess, I answered correctly by providing him property to check auth type.
  • shaqir saiyed
    shaqir saiyed over 5 years
    consider if I don't have any physical device and running in simulator. would I be able to detect then ?
  • swift2geek
    swift2geek over 5 years
    @Surjeet, no your solution doesn't work for me. because its always returns me a type = 0. And 0 is .none. But its not true. Solution of bottom answer from mr leifdan01 worked for me.
  • moliveira
    moliveira over 5 years
    At least on the iOS 12 simulator, inspecting the biometryType works and it is set correctly after the call to canEvaluatePolicy regardless of the canEvaluatePolicy's return value. Per iOS 12 documentation: "This property is set only after you call the canEvaluatePolicy(_:error:) method, and is set no matter what the call returns."
  • Mario Bouchedid
    Mario Bouchedid about 4 years
    Instead of switching on the authContext.biometryType, you can just do the following: Declare your enum enum BiometricType: Int {} and then initialize it in the following way: BiometricType(rawValue: authContext.biometryType.rawValue) and return the value parsed as an optional