提供されたコード (ViewController のボタンによってトリガーされる単純な IBAction) を実際の iPhoneX デバイスで実行すると、evaluatePolicy のエラー ケース (FaceID 認識に何度も失敗して LAError.authenticationFailed) に陥ることができません。このケースはコード内で関数 evaluatePolicyErrorMessage() によって管理されます。
代わりにシミュレーターを使用すると、最初の「Try FaceID again」アラートの後、アラートの「Try FaceID again」ボタンをタップせずに、 LAError.authenticationFailed を 3 回選択して Hardware -> FaceID -> Non-matching Face に陥ることがあり ます。それ自体: Hardware -> FaceID -> Non-matching Faceの選択を 3 回繰り返すだけです。
誰かが私のコードで何がうまくいかないのか教えてもらえますか? かなり冗長で申し訳ありませんが、すべての可能なケースとサブケースを一致させようとしました。
このプロジェクトへのリンクは次のとおりです: https://github.com/giurobrossi/testBiometricAuthentication.git
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func authButton(_ sender: Any) {
let context = LAContext()
if #available(iOS 10, *) {
context.localizedCancelTitle = "Cancel"
}
var canEvaluateError: NSError?
var message = ""
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &canEvaluateError) {
if #available(iOS 11.0, *) {
switch context.biometryType.rawValue {
case 0:
message = "Device doesn't have a biometry available"
case 1:
message = "Device has got TouchID"
case 2:
message = "Device has got FaceID"
default:
message = "biometry device not recognized"
}
} else {
message = "Device is eligible to evaluate the authentication. (iOS < 11)"
}
//self.presentAlertMainThread(message: message)
print(message)
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Let's authenticate!" ) { success, evaluateError in
if success {
self.presentAlertMainThread(message: "Login succesful !")
}
else {
message = self.evaluatePolicyErrorMessage(errorCode: (evaluateError! as NSError).code)
self.presentAlertMainThread(message: message )
print("EvaluatePolicy Error: \(evaluateError!._code)")
}
}
}
else {
message = canEvaluatePolicyErrorMessage(errorCode: (canEvaluateError?.code)!, context: context)
self.presentAlertMainThread(message: message)
print("CanEvaluatePolicy Error: \(String(describing: canEvaluateError?.code ))")
}
}
}
extension ViewController {
func presentAlertMainThread(message: String) {
DispatchQueue.main.async(execute: {
let alertController = UIAlertController(title: "Attenzione", message: message, preferredStyle: UIAlertControllerStyle.alert)
self.present(alertController, animated: true, completion: nil)
let defaultAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
alertController.addAction(defaultAction)
})
}
func canEvaluatePolicyErrorMessage(errorCode: Int, context: LAContext) -> String {
var message = ""
if #available(iOS 11.0, *) {
switch errorCode {
// -6
case LAError.biometryNotAvailable.rawValue:
switch context.biometryType.rawValue {
case 0: // non capita mail (iOS >= 11)
message = "Device does not have any biometry available"
case 1:
message = "Device has got TouchID but the permission is false"
case 2:
message = "Device has got FaceID but the permission is false"
default:
message = "biometry sensor not recognized"
}
// TBD
case LAError.biometryLockout.rawValue:
switch context.biometryType.rawValue {
case 0:
message = "Device does not have any biometry available"
case 1:
message = "Device has got TouchID but the biometry is locked"
case 2:
message = "Device has got FaceID but the biometry is locked"
default:
message = "biometry sensor not recognized"
}
// -7
case LAError.biometryNotEnrolled.rawValue:
switch context.biometryType.rawValue {
case 0:
message = "Device does not have any biometry available"
case 1:
message = "Device has got TouchID but the biometry is not enrolled"
case 2:
message = "Device has got FaceID but the biometry is not enrolled"
default:
message = "biometry sensor not recognized"
}
default:
message = "Policy Error type not managed."
}
}
else if #available(iOS 9.0, *) {
switch errorCode {
// TBD
case LAError.touchIDLockout.rawValue:
if #available(iOS 11.0, *) {
switch context.biometryType.rawValue {
case 0:
message = "Device does not have any biometry available"
case 1:
message = "Device has got TouchID but the biometry is locked"
case 2:
message = "Device has got FaceID but the biometry is locked"
default:
message = "biometry sensor not recognized"
}
} else {
message = "Too much auth. attempts by TouchID (9.0 <= iOS < 11.0) - Auth. Blocked"
}
// -6
case LAError.touchIDNotAvailable.rawValue:
if #available(iOS 11.0, *) {
switch context.biometryType.rawValue {
case 0:
message = "Device does not have any biometry available"
case 1:
message = "Device has got TouchID but the permission is false"
case 2:
message = "Device has got FaceID but the permission is false"
default:
message = "biometry sensor not recognized"
}
} else {
message = "Device does not have TouchID available (9.0 <= iOS < 11.0)"
}
// -7
case LAError.touchIDNotEnrolled.rawValue:
if #available(iOS 11.0, *) {
switch context.biometryType.rawValue {
case 0:
message = "Device does not have any biometry available"
case 1:
message = "Device has got TouchID but the biometry is not enrolled"
case 2:
message = "Device has got FaceID but the biometry is not enrolled"
default:
message = "biometry sensor not recognized"
}
} else {
message = "TouchID not 'enrolled' (9.0 <= iOS < 11.0)"
}
default:
message = "Policy Error type not managed."
}
}
else {
// 8.0 <= iOS < 9.0
print("canEvaluatePolicy Error: \(errorCode.description)")
}
return message;
}
func evaluatePolicyErrorMessage(errorCode: Int) -> String {
var message = ""
if #available(iOS 9.0, *) {
switch errorCode {
// -1
case LAError.authenticationFailed.rawValue:
message = "Authentication Failed"
// -9
case LAError.appCancel.rawValue:
message = "Authentication cancelled by the app"
// TBD
case LAError.invalidContext.rawValue:
message = "LAContext passed to this call has been previously invalidated"
// TBD
case LAError.notInteractive.rawValue:
message = "Authentication failed, because it would require showing UI which has been forbidden by using interactionNotAllowed property"
// TBD
case LAError.passcodeNotSet.rawValue:
message = "Passcode not set"
// -4
case LAError.systemCancel.rawValue:
message = "Authentication cancelled by iOS"
// -2
case LAError.userCancel.rawValue:
message = "Authentication cancelled by the user"
// -3
case LAError.userFallback.rawValue:
message = "Fallback selected by the user
default:
message = "Error not managed"
}
} else {
message = "Failed Authentication (8.0 <= iOS < 9.0) - Code: \(errorCode.description)"
}
return message
}
}