touchIDLockout deprecated in iOS 11.0

Viewed 5388

When compiling my Application with Xcode 9 for IOS11 I get the following warnings:

warning: 'touchIDLockout' was deprecated in iOS 11.0: use LAErrorBiometryLockout

warning: 'touchIDNotEnrolled' was deprecated in iOS 11.0: use LAErrorBiometryNotEnrolled

warning: 'touchIDNotAvailable' was deprecated in iOS 11.0: use LAErrorBiometryNotAvailable

I'm using touchID but I'm not using touchIdLockout...cste and the touchID is working correctly.

How can I remove these warnings?


Edit (not by the original author):

I tracked this down to a single cause. It's enough to reference LAError from the LocalAuthentication framework in my code to make these warnings appear.

Steps to reproduce (tried in Xcode 9.2):

  1. Create a new iOS app (Single View template). Note the iOS Deployment Target is set to iOS 11.2.
  2. Add these lines to AppDelegate.swift:

    import LocalAuthentication
    

    And a single line in appDidFinishLaunching:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let _: LAError? = nil
        return true
    }
    
  3. Build the app.

The let _: LAError? = nil line is enough to make the three warnings appear. The warnings are not associated with any particular line of code, though. They appear in the build log without any file/line reference:

<unknown>:0: warning: 'touchIDLockout' was deprecated in iOS 11.0: use LAErrorBiometryLockout
<unknown>:0: warning: 'touchIDNotEnrolled' was deprecated in iOS 11.0: use LAErrorBiometryNotEnrolled
<unknown>:0: warning: 'touchIDNotAvailable' was deprecated in iOS 11.0: use LAErrorBiometryNotAvailable

Here's a screenshot: Screenshot of the warnings in Xcode

And a sample project: Sample project for download (Xcode 9.2)

For reference, I reported this to Apple. Radar #36028653.

4 Answers

Yes, these are new warnings which are present as Apple moves to iOS 11 and FaceID. Most likely, you are checking to see if the biometric hardware is not locked out, has enrolled fingerprints, and the device has the supporting hardware.

Here an example set up:

import LocalAuthentication

...

var authContext = LAContext()
var biometricsError: NSError?
authContext?.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &biometricsError)

Up to iOS 10, one would run checks like this:

if biometricsError?.code == LAError.touchIDNotAvailable.rawValue {
  // No hardware
}

if biometricsError?.code == LAError.touchIDNotEnrolled.rawValue {
  // No fingerprints
}

if biometricsError?.code == LAError.touchIDLockout.rawValue {
  // Locked out
}

Note: iOS 11 introduced a slight variant of the above code. Instead of using LAError.touchID for each error property, they introduced LAError.biometry. Therefore you would have: biometryNotAvailable, biometryNotEnrolled, and biometryLockout.

Apple seems to prefer this approach, instead:

if biometricsError?.code == Int(kLAErrorBiometryNotAvailable) {
  // No hardware
}

if biometricsError?.code == Int(kLAErrorBiometryNotEnrolled) {
  // No fingerprints
}

if biometricsError?.code == Int(kLAErrorBiometryLockout) {
  // Locked out
}

This method gets rid of Xcode's warnings.

As has been noted, this is a bug in the compiler. The Swift team are aware and you might want to go and vote for the bug. At the same time, add a watch on it so that you can remove the following workaround when it has been fixed.

What you need to do in order to not get the warnings is: do not mention LAError. Think of LAError as Voldemort.

Instead, use Objective-C style error checking. All Error enums map to an NSError, which are built up from a domain and a code. The constants to compare these with are also exported to Swift. They can be named without warnings. So your code might look a little (hopefully, very little) like this.

let context = LAContext()
let text = "Authenticate, please!"
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: text) { (success, error) in
    if success {
        print("")
    } else {
        guard let error = error else {
            return print("Should not happen according to the docs!")
        }
        let nsError = error as NSError
        switch nsError.domain {
        case kLAErrorDomain:
            switch nsError.code {
            case Int(kLAErrorUserCancel):
                print("User cancelled.")
            case Int(kLAErrorBiometryLockout):
                print("Biometry lockout.")
            default:
                print("Unhandled error.")
            }
        default:
            print("Unhandled error domain. Probably will not happen.")
        }
    }
}

I too struggled with this for a very long time. Oliver's answer led me to a solution that worked for me, but just in case there are others that are still having this issue - it seems that ANY reference to the old LAError values will produce the warnings listed above.

For example, this simple code produces the warnings. Remove ALL reference to the old codes and use Oliver's approach.

func evaluateAuthenticationPolicyMessageForLA(errorCode: Int) -> String {
    var message = ""

    switch errorCode {
    case LAError.authenticationFailed.rawValue:
        message = "The user failed to provide valid credentials"
    default:
        message = "Can't use any version of old LAError"
    }

    return message
}//evaluateAuthenticationPolicyMessageForLA

In fact, it can be even simpler. Try this:

func evaluateAuthenticationPolicyMessageForBiometricsError(biometricsError: Int) -> String {

    var message = "I would normally leave this blank"

    if biometricsError == kLAErrorBiometryNotAvailable {
        message = "Biometrics are not available on this device"
    }//if not avail

    if biometricsError == kLAErrorBiometryNotEnrolled {
        message = "Biometrics are not enrolled on this device"
    }//if not enrolled

    if biometricsError == kLAErrorBiometryLockout {
        message = "Biometrics are locked out on this device"
    }//if locked out

    return message

}//evaluateAuthenticationPolicyMessageForBiometricsError
Related