Reputation: 678
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.
Upvotes: 10
Views: 6061
Reputation: 61
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()
}
}
Upvotes: 2
Reputation: 3924
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)
}
Upvotes: 18
Reputation: 6282
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.
}
}
}
Upvotes: 1