Face ID evaluation process not working properly
Asked Answered
M

3

10

I'm trying to get if Face ID or Touch ID succeeded in the function below

func authenticate() -> Bool{

    let context = LAContext()
    var error: NSError?

    guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
        return false
    }
    var returnValue = false
    let reason = "Face ID authentication"
    context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) 
    {
        isAuthorized, error in
        guard isAuthorized == true else {
            return print(error)
        }
        returnValue = true
        print("success")
    }
    return returnValue        
}

but even when it succeeds with this code it skips the returnValue = true is passed later which results a false return. why does this happen? and how can I fix this code to make it work like it is suppose to?

the code above is from this link just in case this person was watching, thank you.

Madra answered 30/8, 2018 at 8:59 Comment(3)
Your code is perfectly fine, implement error handling when you are ready. For now you have a logical mistake where you are attempting to return when its actually doing an async task. Put a break point with last return statement and the one inside evaluatePolicy. You will understand for yourself why returns are failing.Despumate
Thanks for the reply, I'll try it out and try to understand the code more.Madra
Here is my updated answer for this, I hope it will help. stackoverflow.com/a/73891523/14180967Outgeneral
C
18

Working code of Touch ID & Face ID LocalAuthentication

(swift 4.0 & 5.0+ Code)

Note : Privacy - Face ID Usage Description key add in Info.plist

Use

self.Authenticate { (success) in
     print(success)
}

Local Authentication Function

import LocalAuthentication

func Authenticate(completion: @escaping ((Bool) -> ())){
    
    //Create a context
    let authenticationContext = LAContext()
    var error:NSError?
    
    //Check if device have Biometric sensor
    let isValidSensor : Bool = authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
    
    if isValidSensor {
        //Device have BiometricSensor
        //It Supports TouchID
        
        authenticationContext.evaluatePolicy(
            .deviceOwnerAuthenticationWithBiometrics,
            localizedReason: "Touch / Face ID authentication",
            reply: { [unowned self] (success, error) -> Void in
                
                if(success) {
                    // Touch / Face ID recognized success here
                    completion(true)
                } else {
                    //If not recognized then
                    if let error = error {
                        let strMessage = self.errorMessage(errorCode: error._code)
                        if strMessage != ""{
                            self.showAlertWithTitle(title: "Error", message: strMessage)
                        }
                    }
                    completion(false)
                }
        })
    } else {
        
        let strMessage = self.errorMessage(errorCode: (error?._code)!)
        if strMessage != ""{
            self.showAlertWithTitle(title: "Error", message: strMessage)
        }
    }
    
}

Handle Error Codes with Messages

//MARK: TouchID error
func errorMessage(errorCode:Int) -> String{
    
    var strMessage = ""
    
    switch errorCode {
        
    case LAError.Code.authenticationFailed.rawValue:
        strMessage = "Authentication Failed"
        
    case LAError.Code.userCancel.rawValue:
        strMessage = "User Cancel"
        
    case LAError.Code.systemCancel.rawValue:
        strMessage = "System Cancel"
        
    case LAError.Code.passcodeNotSet.rawValue:
        strMessage = "Please goto the Settings & Turn On Passcode"
        
    case LAError.Code.biometryNotAvailable.rawValue:
        strMessage = "TouchI or FaceID DNot Available"
        
    case LAError.Code.biometryNotEnrolled.rawValue:
        strMessage = "TouchID or FaceID Not Enrolled"
        
    case LAError.Code.biometryLockout.rawValue:
        strMessage = "TouchID or FaceID Lockout Please goto the Settings & Turn On Passcode"
        
    case LAError.Code.appCancel.rawValue:
        strMessage = "App Cancel"
        
    case LAError.Code.invalidContext.rawValue:
        strMessage = "Invalid Context"
        
    default:
        strMessage = ""
        
    }
    return strMessage
}

Show alert message

//MARK: Show Alert
func showAlertWithTitle( title:String, message:String ) {
    let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
    
    let actionOk = UIAlertAction(title: "OK", style: .default, handler: nil)
    alert.addAction(actionOk)
    self.present(alert, animated: true, completion: nil)
}
Copulative answered 30/8, 2018 at 9:26 Comment(2)
in your authenticate function, can you show me a way to implement a return bool?Madra
@Madra Check my Updated answer for return bool.Copulative
C
2

enter image description here

Note : Privacy - Face ID Usage Description key add in Info.plist

func canAuthenticate() -> Bool {
    let context = LAContext()
    var error: NSError?
    
    if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
        return true
    } else {
        // Handle the error, if any
        print("Biometric authentication not available: \(error?.localizedDescription ?? "Unknown error")")
        return false
    }
}

func authenticateUser() {
    let context = LAContext()
    
    context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Authenticate to access the app") { (success, authenticationError) in
        DispatchQueue.main.async { [self] in
            if success {
                // Biometric authentication succeeded
                print("Authentication successful")

            } else {
                // Biometric authentication failed or was canceled
                if let error = authenticationError as? LAError {
                    switch error.code {
                    case .userCancel:
                        print("Authentication canceled by the user")
                    case .userFallback:
                        print("User chose to enter password instead")
                        // Handle password entry here if needed
                    default:
                        print("Authentication failed: \(error.localizedDescription)")
                    }
                }
            }
        }
    }
}

Usage

override func viewDidLoad() {
    super.viewDidLoad()
    if canAuthenticate() {
        authenticateUser()
    }
}
Classis answered 26/12, 2023 at 6:54 Comment(0)
D
1

You should use a closure to obtain results of evaluation. Note that the return value for canEvaluatePolicy is a Bool but there is no return value for evaluatePolicy as it accepts a closure.

You can modify your method to include a closure instead of return.

func authenticate(completion: ((Bool) -> ())) {
    ...
    completion(true) // false if it failed.
    ...
}

In other parts of your app, where you earlier used a return value you would now have to use a closure like:

class Foo {
   func test() {
       let isEvaluated = self.authenticate() // Old way
       self.authenticate { success in
           // bool will now indicate whether evaluation was done successfully or not.
       }
   }
}
Despumate answered 30/8, 2018 at 9:15 Comment(2)
so, what can I do to fix this?Madra
Dont use return. Use a closure like mentioned here.Despumate

© 2022 - 2024 — McMap. All rights reserved.