I want to get the string for the current lock type used in the device, whether it is FaceID, touchID or PassCode. Below is my code :-
func getBiometricType() -> String {
var biometricType: Global.BiometricType {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else { return .none }
if #available(iOS 11.0, *) {
switch context.biometryType {
case .touchID:
return .touchID
case .faceID:
return .faceID
case .none:
return .none
}
} else {
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else { return .none }
return context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) ?
.touchID : .PIN
}
}
return biometricType.rawValue
}
But this canEvaluatePolicy only checks whether the device supports biometric or not. Even when the FaceID is not set up yet but the Passcode is enabled, it does not give info regarding the passcode. As I need to show the type enabled is "Passcode". Is there any way to achieve this?
You have to use LAPolicy.deviceOwnerAuthenticationWithBiometrics.
As per apple docs:
If Touch ID or Face ID is not available or not enrolled, policy
evaluation fails. After three unsuccessful Touch ID or Face ID
attempts in a row, policy evaluation will fail. Both Touch ID and Face
ID authentication are disabled after five unsuccessful attempts,
requiring the user to enter the device passcode in order to be
reenabled.
LAPolicy.deviceOwnerAuthentication enables:
If Touch ID or Face ID is available, enrolled, and not disabled, the
user is asked for that first. Otherwise, they are asked to enter the
device passcode. If the device passcode is not enabled, policy
evaluation fails. Passcode authentication is disabled after 6
unsuccessful attempts, with progressively increasing delays between
them.
Related
`Hi everyone, I am new to Swift and following a biometrics authentication tutorial to click a button and use Face ID or Touch ID.
Nothing happens when the biometric authentication button is pressed because canEvaluate etc do not get set to true. Details below.
variables and the relevant function
private(set) var context = LAContext()
#Published private(set) var biometryType: LABiometryType = .none
private(set) var canEvaluatePolicy = false
#Published private(set) var isAuthenticated = false`
func getBiometryType(){
context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
biometryType = context.biometryType
}
func authenticatedWithBiometrics() async {
context = LAContext()
print("inside auth func")
if canEvaluatePolicy{
let reason = "to log in to your account"
do{
let success = try await context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason)
print(success)
if success {
DispatchQueue.main.async {
self.isAuthenticated = true
print("is authenticated", self.isAuthenticated)
}
}
}
catch{ //if fail to authenticate, throw an error, show it, no biometry
print(error.localizedDescription)
DispatchQueue.main.async {
self.errorDescription = error.localizedDescription
self.showAlert = true
self.biometryType = .none
}
}
}
}
The issue: nothing happens when I click on the button that calls authenticatedwithbiometrics(). The function is called, the issue is that canEvaluatePolicy is FALSE and does not get set to true in getBiometryType. If I manually configure variables canEvaluatePolicy and success to true, we eventually run into the error.localizedDescription -> "biometry is not enrolled".
I've tried different simulators: iPhone 13, 14, 14 Pro, 14Pro Max, and SE which uses TouchID, and that also does nothing.
I've seen someone who had a same problem, but then it was solved by trying different simulators. That did not solve my issue.
I don't think it's an issue with the code because it's literally copied from a working tutorial. Do I need to configure something in the simulator?
Can we test Face ID in simulator? this type of answer is not useful to me because I cannot even evoke the gray popup that initiates the face or touch ID process.
I am using the newest version of xcode.
Thank you. `
I know why now. You have to restart the build after checking the "Enrolled face" button for the cue to show up.
You can used biometric authentication with simulator but you have to ensure you check enrolled in feature > FaceId > Enrolled after this you can check both scenarios Matching or Non Matching
How I can check is the Face ID is Enabled/ Disabled in settings page?
I checked in LAContext error type. It return
Code=-6 "User has denied the use of biometry for this app."
But the device do not support biometric authentication also getting same error code. (kLAErrorTouchIDNotAvailable -6)
Is any way to find user enabled/disabled Face ID in settings?
first add a method for your users to enable faceid/Touch ID using local authentication. adding this method will allow users to enable and disable biometric id in settings. you can use a whatever method you'd like, button, segment controller or view and using a prompt you'll ask the users permission to enable biometric id. This code may help:
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Enable FaceID!"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) {
[unowned self] (success, authenticationError) in
DispatchQueue.main.async {
if success {
self.unlockSecretMessage()
} else {
// error
}
}
}
} else {
// no biometry
}
In our app, the user has to register to the device biometry in order to use it for authentication.
The registration text and legal notes are according to the relevant biometry (register to touch ID or register to face ID)
As far as I found, the biometry type is obtainable via the LAContext, but if the user denies usage of biometry, then the context returns biometryType=.none
Any ideas other that asking for the screen size and comparing to iphone X (bad bad code)?
static fileprivate var biometryType: DSLocalAuthenticationBiometryType {
let context = LAContext()
var error: NSError?
let _ = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
if #available(iOS 11.0, *) {
return context.biometryType == .typeFaceID ? .typeFaceID : .none
}
else {
return .none
}
}
Thanks
I've got the same identical issue, and I've just found out that if you evaluate against the key LAPolicyDeviceOwnerAuthentication instead of LAPolicyDeviceOwnerAuthenticationWithBiometrics, even after the user declined the permission, the evaluation succeeds and you get the correct biometryType. Your code would be like
static fileprivate var biometryType: DSLocalAuthenticationBiometryType {
let context = LAContext()
var error: NSError?
let _ = context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)
if #available(iOS 11.0, *) {
return context.biometryType == .typeFaceID ? .typeFaceID : .none
}
else {
return .none
}
}
NOTE: on devices without touch id and face id, it still returning YES, so you would not know whether the device really has a biometric hw or not with iOS lower than 11 (which do not expose the property biometriyType)
Update
For devices with iOS version 10 or lower, you can use the
LAPolicyDeviceOwnerAuthenticationWithBiometrics as usual, it will behave correctly (returning true whether the device supports the touch Id), so it's just a matter of differentiating the running OS version :)
Let me know if it works :)
Best
I want to use TouchID in my app and I've found some weird behaviour. So in general when TouchID alert is shown it and user try to authenticate with fingerprint canEvaluatePolicy will fail only after 3 attempts but if user go to Settings>Touch ID & Passcode and turn off "use touch id for: iPhone Unlock" canEvaluatePolicy will fail after first attempt with error message "Biometry is disabled for unlock.". Does anyone know is it a bug or it is by design.
Also looks like it happens only on iOS 11.
Here is code that i use for TouchID configuration
var error: NSError?
context.localizedFallbackTitle = "fallback title"
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
DispatchQueue.main.async {
if success {
// success case
}
else {
// error handling
}
}
}
}
else {
// error handling
}
Actually, I building an app which contains local authentication.
My code so far:
func authenticateUser() {
let authenticationContext = LAContext()
var error: NSError?
let reasonString = "Touch the Touch ID sensor to unlock."
// Check if the device can evaluate the policy.
if authenticationContext.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error) {
authenticationContext.evaluatePolicy( .deviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success, evalPolicyError) in
if success {
print("success")
} else {
if let evaluateError = error as NSError? {
// enter password using system UI
}
}
})
} else {
print("toch id not available")
// enter password using system UI
}
}
My problem is I want to use the passcode lock scene when the app doesn't has touch ID or invalid finger print.
Like below Image:
How can I do it?
You should use .deviceOwnerAuthentication instead of .deviceOwnerAuthenticationWithBiometrics to evaluate policy. With this parameter the system uses biometric authentication if available else it presents passcode screen. And if the biometric authentication is available but fails, a fallback button redirect to the passcode screen. See documentation :
If Touch ID or Face ID is available, enrolled, and not disabled, the user is asked for that first. Otherwise, they are asked to enter the device passcode.
Tapping the fallback button switches the authentication method to ask the user for the device passcode.
So your code will be:
func authenticateUser() {
let authenticationContext = LAContext()
var error: NSError?
let reasonString = "Touch the Touch ID sensor to unlock."
// Check if the device can evaluate the policy.
if authenticationContext.canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: &error) {
authenticationContext.evaluatePolicy( .deviceOwnerAuthentication, localizedReason: reasonString, reply: { (success, evalPolicyError) in
if success {
print("success")
} else {
// Handle evaluation failure or cancel
}
})
} else {
print("passcode not set")
}
}
At this point, I am afraid that you cannot access this passcode lock screen into your app, it is related to the iOS itself. You might need to build your own custom view controller to look/behave as the passcode lock scene (with Touch ID). I would suggest to use a library for achieving this, personally, I've tried PasscodeLock and it works fine for me.