Is there a way to detect if the user has declined biometrics (faceID) after using "LAContext().evaluatePolicy(.deviceOwnerAuthentication, localizedReason: someReason)"?
For example,
- User logs in first time they are prompted with if the app can use their biometrics
- User declines biometrics then prompted for passcode
- User declines passcode
It appears canEvaluatePolicy returns true for "LAContext().canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)" until I close and reopen the app.
I want to be able to alert the user that they can turn faceID on in their app settings.
CodePudding user response:
// Declare You're own Error type to handle possible errors
enum BiometryError: Swift.Error, LocalizedError {
case unavailable, // Biometry unavailable
failed, // Can't verify
cancel, // User pressed cancel
failback, // User choose password
locked, // Biometry is locked
enrolled, // Biometry not setup
succsess // Success
init(error: LAError) {
switch error {
case LAError.authenticationFailed:
self = .failed
case LAError.userCancel:
self = .cancel
case LAError.userFallback:
self = .failback
case LAError.biometryNotAvailable:
self = .unavailable
case LAError.biometryNotEnrolled:
self = .enrolled
case LAError.biometryLockout:
self = .locked
default:
self = .unavailable
}
}
public var errorDescription: String? {
switch self {
case .unavailable:
return "Unavailable"
case .failed:
return "Failed"
case .cancel:
return "Cancel"
case .failback:
return "Failback"
case .locked:
return "Locked"
case .enrolled:
return "Enrolled"
case .succsess:
return "Succsess"
}
}
}
// Authenticate method
func authenticateBiometry(reply callback: @escaping (Bool, BiometryError?) -> Void) {
LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "reason") { success, error in
if let error = error as? LAError, !success {
// Here in this callback You can handle error and show alert if You want
callback(false, BiometryError(error: error))
} else {
// Success
callback(true, nil)
}
}
}
CodePudding user response:
There does not appear to be a way to determine the case you describe in your question.
I note that the documentation for canEvaluatePolicy
states:
Don’t store the return value from this method because it might change as a result of changes in the system. For example, a user might disable Touch ID after you call this method. However, the reported value does remain consistent until your app enters the background.
However, in testing it seems that even putting the app into the background does not change the value returned by canEvaluatePolicy
.
As you note in your question, the value returned does not change until the app is relaunched. You will also see that if you go into preferences and toggle the biometric setting for your app then your app is actually relaunched. This same process happens for other privacy-related settings as well.
You can offer biometric authentication on subsequent launches if you determine that it has been denied, but you should be wary about bugging the user when they have already made a decision.
The other approach you can use is to try LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "reason")
and if that fails due to biometrics being denied, retry the authentication with LAContext().evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "reason")
which will immediately prompt for a passcode.