Determine if an iOS device supports TouchID without setting passcode

20,060

Solution 1

In conclusion of the discussion below, for the time being it is not possible to determine whether a device actually supports TouchID or not when the user hasn't set up passcode on their device.

I have reported this TouchID flaw on the Apple bug reporter. Those who want to follow the issue can see it on Open Radar here: http://www.openradar.me/20342024

Thanks @rckoenes for the input :)

EDIT

Turns out that someone has reported a similar issue already (#18364575). Here is Apple's reply regarding the issue:

"Engineering has determined that this issue behaves as intended based on the following information:

If passcode is not set, you will not be able to detect Touch ID presence. Once the passcode is set, canEvaluatePolicy will eventually return LAErrorTouchIDNotAvailable or LAErrorTouchIdNotEnrolled and you will be able to detect Touch ID presence/state.

If users have disabled passcode on phone with Touch ID, they knew that they will not be able to use Touch ID, so the apps don't need to detect Touch ID presence or promote Touch ID based features. "

So..... the final answer from Apple is No. :(

Note: similar StackOverflow question from the person who reported this -> iOS8 check if device has Touch ID (wonder why I didn't find this question before despite my extensive searching...)

Solution 2

The correct way to detect if TouchID is available:

BOOL hasTouchID = NO;
// if the LAContext class is available
if ([LAContext class]) {
    LAContext *context = [LAContext new];
    NSError *error = nil;
    hasTouchId = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
}

Sorry it is in Objective-C, you might have to translate it to C#.

You should refrain from checking the system version and just check whether or not the class or methods are available.

Solution 3

I know this is a question from last year, but this solution does not make what you need? (Swift code)

if #available(iOS 8.0, *) {
    var error: NSError?
    let hasTouchID = LAContext().canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error)

    //Show the touch id option if the device has touch id hardware feature (even if the passcode is not set or touch id is not enrolled)
    if(hasTouchID || (error?.code != LAError.TouchIDNotAvailable.rawValue)) {
        touchIDContentView.hidden = false
    } 
}

Then, when the user presses the button to log in with touch id:

@IBAction func loginWithTouchId() {
    let context = LAContext()

    var error: NSError?
    let reasonString = "Log in with Touch ID"

    if (context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error)) {
        [context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in
            //Has touch id. Treat the success boolean
        })]
    } else { 
        //Then, if the user has touch id but is not enrolled or the passcode is not set, show a alert message
        switch error!.code{

        case LAError.TouchIDNotEnrolled.rawValue:
            //Show alert message to inform that touch id is not enrolled
            break

        case LAError.PasscodeNotSet.rawValue:
            //Show alert message to inform that passcode is not set
            break

        default:
            // The LAError.TouchIDNotAvailable case.
            // Will not catch here, because if not available, the option will not visible
        }
    }
}

Hope it helps!

Solution 4

For Objective C
It works great on all devices without checking device version.

- (void)canAuthenticatedByTouchID{
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = touchIDRequestReason;

if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
 }else{
    switch (authError.code) {
        case kLAErrorTouchIDNotAvailable:
            [labelNotSupportTouchID setHidden:NO];
            [switchBtn setHidden:YES];
            [labelEnableTouchid setHidden:YES];
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                [self showAlertMessage:@"EyeCheck Pro" message:@"Device does not support Touch ID Service."];
            });

            break;
    }
  }
}

Solution 5

Following is the way by which you can identify whether Touch Id or Face ID is supported on the device

open class LocalAuth: NSObject {

    public static let shared = LocalAuth()

    private override init() {}

    var laContext = LAContext()

    func canAuthenticate() -> Bool {
        var error: NSError?
        let hasTouchId = laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
        return hasTouchId
    }

    func hasTouchId() -> Bool {
        if canAuthenticate() && laContext.biometryType == .touchID {
            return true
        }
        return false
    }

    func hasFaceId() -> Bool {
        if canAuthenticate() && laContext.biometryType == .faceID {
            return true
        }
        return false
    }

}

And following is the Usage of the above-shared code

if LocalAuth.shared.hasTouchId() {
    print("Has Touch Id")
} else if LocalAuth.shared.hasFaceId() {
    print("Has Face Id")
} else {
    print("Device does not have Biometric Authentication Method")
}
Share:
20,060

Related videos on Youtube

ABVincita
Author by

ABVincita

An IT enthusiast fresh out of uni and is currently working as a mobile developer. Love programming and food! xD

Updated on January 05, 2022

Comments

  • ABVincita
    ABVincita over 2 years

    I'm currently developing an iOS app that enables users to log in to the app using TouchID, but firstly they must set up a password inside the app first. Problem is, to show the setup password option to enable the TouchID login, I need to detect if the iOS device supports TouchID.

    Using the LAContext and canEvaluatePolicy (like the answers in here If Device Supports Touch ID), I am able to determine whether the current device supports TouchID if the user has set up passcode on their iOS device. Here is a my code snippet (I'm using Xamarin, so it's in C#):

    static bool DeviceSupportsTouchID () 
    {
        if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
        {
            var context = new LAContext();
            NSError authError;
            bool touchIDSetOnDevice = context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out authError);
    
            return (touchIDSetOnDevice || (LAStatus) Convert.ToInt16(authError.Code) != LAStatus.TouchIDNotAvailable);
        }
    
        return false;
    }
    

    If the user has not set up the device passcode, the authError will just return "PasscodeNotSet" error regardless of whether the device actually supports TouchID or not.

    If the user's device supports TouchID, I want to always show the TouchID option in my app regardless of whether the user has set up passcode on their device (I will just warn the user to setup passcode on their device first). Vice versa, if the user's device doesn't support TouchID, I obviously don't want to show the TouchID option in my app.

    So my question is, is there a nice way to consistently determine whether an iOS device supports TouchID regardless of whether the user has set up passcode on their device?

    The only workaround I can think of is to determine the architecture of the device (which is answered in Determine if iOS device is 32- or 64-bit), as TouchID is only supported on devices with 64-bit architecture. However, I'm looking if there's any nicer way to do this.

  • ABVincita
    ABVincita about 9 years
    Thanks for your answer, but I'm afraid it's not the solution I'm looking for. As far as I know, the LAContext class is available on all devices running iOS8. But in cases where the device runs iOS8 but don't have TouchID, then the if clause will be true and NSError will still return PasscodeNotSet... Correct me if I'm mistaken here :)
  • rckoenes
    rckoenes about 9 years
    Yes that might be, but the hasTouchId will be false thus TouchID is not available for you as a developers to use.
  • ABVincita
    ABVincita about 9 years
    Yes the hasTouchId will be false as well-- but then there's no way for me to determine if the device actually supports TouchID or not. As stated in my question above, the main problem here is when the user has not set up passcode on their device; hasTouchId will be false and NSError will be PasscodeNotSet regardless of whether the device actually supports TouchID or not.
  • rckoenes
    rckoenes about 9 years
    True, but why detect the whether or not the device has a touchID if you are unable to use it?
  • ABVincita
    ABVincita about 9 years
    Good point; my answer is, that's my team's design decision that we will still show the TouchID option for all devices that have TouchID even if the user hasn't set up passcode yet. We will give warnings to the user that they need to set up passcode and fingerprint on their device first, but we want to show that it is possible to use TouchID using their device. We may need to reassess that design decision-- but nevertheless I want to know if such thing is possible :)
  • rckoenes
    rckoenes about 9 years
    Sound like a valid reason, but there are 64 bit devices without a TouchID, like the iPad air en iPad Mini retina. You should submit a bug report to Apple so they can fix this issue in a update of the SDK.
  • ABVincita
    ABVincita about 9 years
    Ah true, I forgot about the 2013 iPad air & iPad mini. So for the time being, the conclusion is that it's not possible to do such thing. I'll submit a bug report to Apple soon. Thanks @rckoenes!
  • rckoenes
    rckoenes about 9 years
    @ABVincita Post you Radar on open radar aswel and link it here, then we can all submit this feature request to Apple and hopefully get it in the SDK soon.
  • justinkoh
    justinkoh almost 6 years
    This is helpful but please include "#include <sys/sysctl.h>".
  • Naishta
    Naishta over 3 years
    nice and easy flow, except the "let hasTouchId" could be renamed to "let deviceHasBiometry"