How to programmatically check support of 'Face Id' and 'Touch Id'
Asked Answered
S

15

54

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?

Shalloon answered 23/10, 2017 at 10:49 Comment(0)
G
52

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
    }
}
Geranium answered 23/10, 2017 at 10:55 Comment(7)
In the just released Xcode 9.2 beta, with iOS 11.2, the LABiometryType enum values have changed to faceID and touchID.Enthusiasm
@Enthusiasm Thanks for reminding me. I have updated answer.Geranium
can we detect whether device supports Face ID or touch ID using only simulator ?Mysterious
Yes, You can test this using simulator. Select Simulator -> Hardware -> Touch ID -> Cases This will provide support for both based on simulator.Geranium
you have not answered the question. the question not about what it is? but how to/>Carcanet
@Carcanet 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.Geranium
@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.Carcanet
N
77

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
}
Nahshun answered 24/10, 2017 at 21:14 Comment(9)
Thanks for this. Exactly what I was looking for :)Stirk
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.Howlet
@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.Stadia
@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.Howlet
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/…Stadia
you must import LocalAuthenticationWhap
this question is the right one and should be chosen like accepted.Carcanet
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."Bandoline
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 optionalEcology
G
52

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
    }
}
Geranium answered 23/10, 2017 at 10:55 Comment(7)
In the just released Xcode 9.2 beta, with iOS 11.2, the LABiometryType enum values have changed to faceID and touchID.Enthusiasm
@Enthusiasm Thanks for reminding me. I have updated answer.Geranium
can we detect whether device supports Face ID or touch ID using only simulator ?Mysterious
Yes, You can test this using simulator. Select Simulator -> Hardware -> Touch ID -> Cases This will provide support for both based on simulator.Geranium
you have not answered the question. the question not about what it is? but how to/>Carcanet
@Carcanet 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.Geranium
@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.Carcanet
S
16

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
Synchrocyclotron answered 15/8, 2018 at 14:8 Comment(0)
S
7

Objective C :)

/** Only interesting devices are enumerated here. To change view constraints depending
 on screen height. Or the top notch for iPhone X
 */
typedef NS_ENUM(NSUInteger, BPDeviceType) {
    BPDeviceTypeUnknown,
    BPDeviceTypeiPhone4,
    BPDeviceTypeiPhone5,
    BPDeviceTypeiPhone6,
    BPDeviceTypeiPhone6Plus,
    BPDeviceTypeiPhone7,
    BPDeviceTypeiPhone7Plus,
    BPDeviceTypeiPhoneX,
    BPDeviceTypeiPad
};

+ (BPDeviceType)getDeviceType {
    double screenHeight = [[UIScreen mainScreen] bounds].size.height;
    if(UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad)
    {
        return BPDeviceTypeiPad;

    } else if (UI_USER_INTERFACE_IDIOM()== UIUserInterfaceIdiomPhone)
    {
        if (@available(iOS 11, *)) {
            UIEdgeInsets insets = [UIApplication sharedApplication].delegate.window.safeAreaInsets;
            if (insets.top > 0) {
                return BPDeviceTypeiPhoneX;
            }
        }

        if(screenHeight == 480) {
            return BPDeviceTypeiPhone4;
        } else if (screenHeight == 568) {
            return BPDeviceTypeiPhone5;
        } else if (screenHeight == 667) {
            return BPDeviceTypeiPhone6;
        } else if (screenHeight == 736) {
            return BPDeviceTypeiPhone6Plus;
        }
    }
    return BPDeviceTypeUnknown;
}

+ (BOOL) isBiometricIDAvailable {
    if (![LAContext class]) return NO;

    LAContext *myContext = [[LAContext alloc] init];
    NSError *authError = nil;
    if (![myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
        NSLog(@"%@", [authError localizedDescription]);
        return NO;
    }
    return YES;
}

+ (BOOL) isTouchIDAvailable {
    if (![LAContext class]) return NO;

    LAContext *myContext = [[LAContext alloc] init];
    NSError *authError = nil;
    if (![myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
        NSLog(@"%@", [authError localizedDescription]);
        return NO;
        // if (authError.code == LAErrorTouchIDNotAvailable) {}
    }

    if (@available(iOS 11.0, *)) {
        if (myContext.biometryType == LABiometryTypeTouchID){
            return YES;
        } else {
            return NO;
        }
    } else {
        return YES;
    }
}

+ (BOOL) supportFaceID {
    return [BPDeviceInfo getDeviceType] == BPDeviceTypeiPhoneX;
}

+ (BOOL) isFaceIDAvailable {
    if (![LAContext class]) return NO;

    LAContext *myContext = [[LAContext alloc] init];
    NSError *authError = nil;
    if (![myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
        NSLog(@"%@", [authError localizedDescription]);
        return NO;
    }

    if (@available(iOS 11.0, *)) {
        if (myContext.biometryType == LABiometryTypeFaceID){
            return YES;
        } else {
            return NO;
        }
    } else {
        return NO;
    }
}
Sleight answered 21/2, 2018 at 8:21 Comment(0)
A
7

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
}
Aluminous answered 9/8, 2018 at 15:33 Comment(3)
can we detect whether device supports Face ID or touch ID using only simulator ?Mysterious
@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.Aluminous
consider if I don't have any physical device and running in simulator. would I be able to detect then ?Mysterious
D
7

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")
        }
 }
Diary answered 29/6, 2019 at 10:0 Comment(0)
S
5

Here is one more way via the property (for example, on your access instance).

import LocalAuthentication


enum BiometricType {
    case none
    case touchID
    case faceID
}

var biometricType: BiometricType {
    get {
        let context = LAContext()
        var error: NSError?

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

        if #available(iOS 11.0, *) {
            switch context.biometryType {
            case .none:
                return .none
            case .typeTouchID:
                return .touchID
            case .typeFaceID:
                return .faceID
            }
        } else {
            return  .touchID
        }
    }
}
Shig answered 29/11, 2017 at 14:20 Comment(1)
can we detect whether device supports Face ID or touch ID using only simulator ?Mysterious
C
5

Here is my "helper class", it includes passcode also

enum BiometryType: String {
    case none = "None"
    case faceID = "Face ID"
    case touchID = "Touch ID"
    case passcode = "Passcode"
}


var biometryType: BiometryType {
    let myContext = LAContext()

    let hasAuthenticationBiometrics = myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
    let hasAuthentication = myContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)

    if #available(iOS 11.0, *) {
        if hasAuthentication {
            if hasAuthenticationBiometrics {
                switch myContext.biometryType {
                case .none: return .none
                case .faceID: return .faceID
                case .touchID: return .touchID
                }
            } else {
                return .passcode
            }
        } else {
            return .none
        }
    } else {
        if hasAuthentication {
            if hasAuthenticationBiometrics {
                return .touchID
            } else {
                return .passcode
            }
        } else {
            return .none
        }
    }
}
Cleanser answered 14/12, 2018 at 11:25 Comment(0)
S
3
-(BOOL)faceIDAvailable {
        LAContext *myContext = [[LAContext alloc] init];
        NSError *authError = nil;

        if (@available(iOS 11.0, *)) {
            if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError] && myContext.biometryType == LABiometryTypeFaceID) {
                return true;
            }
        }
      return false;

}
-(BOOL)touchIDAvailable {
        LAContext *myContext = [[LAContext alloc] init];
        NSError *authError = nil;

        if (@available(iOS 11.0, *)) {
            if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError] && myContext.biometryType == LABiometryTypeTouchID) {
                return true;
            }
        }
      return false;

}
Staggers answered 22/4, 2020 at 9:47 Comment(0)
K
3

You NEED to add @unknown to the cases!!!

In newer Versions(Xcode 13...) you may have to specify "unknown"

#warning("Handle new Biometric type") doesn't return a value

import LocalAuthentication

class BiometricType{


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
        @unknown default:
            return .unknown
        }
    } else {
        return authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touch : .none
    }
}

enum BiometricType {
    case none
    case touch
    case face
    case unknown
}

}
Kaiser answered 14/10, 2021 at 7:24 Comment(0)
O
1

This code builds without warnings on Xcode 9.2-9.4 (see comments for 9.1):

@objc let biometricsAuthPolicy = LAPolicy.deviceOwnerAuthenticationWithBiometrics

@objc func supportsFaceID() -> Bool {
    if #available(iOS 11.0, *) {
        return biometryType() == .faceID // return biometryType() == .typeFaceID for Xcode 9.1
    }
    return false
}

@objc func supportsTouchID() -> Bool {
    if #available(iOS 11.0, *) {
        return biometryType() == .touchID // return biometryType() == .typeTouchID for Xcode 9.1
    }

    let context = LAContext()
    return context.canEvaluatePolicy(biometricsAuthPolicy, error: nil)
}

@objc @available(iOS 11.0, *)
func biometryType() -> LABiometryType {
    var error: NSError?
    let context = LAContext()

    guard context.canEvaluatePolicy(biometricsAuthPolicy, error: &error) else {
        if #available(iOS 11.2, *) {
            return .none
        }
        return LABiometryType.LABiometryNone // return LABiometryType.none for Xcode 9.1
    }

    return context.biometryType
}
Odometer answered 25/6, 2018 at 11:56 Comment(0)
C
1

From @Markicevic extension, but ignoring cases where user is not enrolled, etc...

extension LAContext {

enum BiometricType: String {
    case none = ""
    case touchID = "Touch ID"
    case faceID = "Face ID"
}

static var biometricType: BiometricType {
    var error: NSError?

    let context = LAContext()

    _ = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)

    if error?.code == LAError.Code.touchIDNotAvailable.rawValue {
        return .none
    }

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

}

Calvinna answered 6/2, 2019 at 19:6 Comment(0)
I
1

update for swift 5, switch flow need a default condition.

import Foundation
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 through fabric
            return .none
        }

        if #available(iOS 11.0, *) {
            switch self.biometryType {
            case .touchID:
                return .touchID
            case .faceID:
                return .faceID
            default:
                return .none
            }
        }

        return self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
    }

}

test case is in the below

// need to import LocalAuthentication in the calling file
// import LocalAuthentication
let currentType = LAContext().biometricType
print("biometry type > \(currentType)")
// biometry type > touchID

If you want to test in simulator, you need to enrolled touchId/faceId.
Simulator > Hardware > Touch ID/Face ID > Enrolled. enter image description here

Ihab answered 10/6, 2020 at 9:1 Comment(0)
P
1

Please refer to the apple sample code provided for "Logging a User into Your App with Face ID or Touch ID", which will be helpful to understand the authentication easily.

Apple Sample Code link - https://docs-assets.developer.apple.com/published/fbfd13f4d9/LoggingAUserIntoYourAppWithFaceIDOrTouchID.zip

Read the detailed explanation of the sample code on the below link. https://developer.apple.com/documentation/localauthentication/logging_a_user_into_your_app_with_face_id_or_touch_id

Poesy answered 18/6, 2020 at 5:36 Comment(0)
T
1

Started testing some new apps on the 12 Pro and realised my published apps only have Touch ID and not Face ID.

I came here and saw all of this so I started trying to change my Touch ID code but all I needed to do was add the privacy key to the info.plist.

Information Property List ➕

Then scroll down to: Privacy- Face ID Usage Description, (Type: String), (Value: YES)

Too easy😀

Typology answered 22/12, 2020 at 4:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.