LAContext evaluatePolicy not showing TouchID prompt - ios

func authenticateBiometry(completion: #escaping ErrorHandler) {
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: " ") { success, error in
guard let error = error else {
if success {
completion(nil)
}
return
}
completion(error)
}
}
But it prompts for touchId/faceId only the first time. What can I do to ask for it for example every time when I tap button? Let's say every 15 seconds.

Just tested locally and it works for me, this is the only way I found. I saw your comment above but I will put an answer here because probably someone will not find it ugly haha :).
I took some time to google some kind of reset method in LAContext class, but didn't find anything.
The solution was to reset the LAContext at the beginning of the method called on button tap:
func authenticateBiometry(completion: #escaping ErrorHandler) {
context = LAContext() //THIS, implying that context is declared as `var`
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: " ") { success, error in
guard let error = error else {
if success {
completion(nil)
}
return
}
completion(error)
}
}
You will be able to prompt face/touch ID on each button click, as soon as one ends.

Related

How to check if user revoked HealthKit permissions through Settings and act in response to it?

I am using Apple's HealthKit to build an app, and am requesting permission from the user to read and write Sleep Data as soon as a View loads using SwiftUI's .onAppear modifier.
It all works fine and I get the data I need using this method.
However, if a user revokes the read and write permissions for my app through Settings, instead of requesting permission again, the app crashes. This is how I have set things up.
#State var authorized: Bool = false
var body: some View {
Text("\(initTextContent)")
.onAppear {
healthstore.getHealthAuthorization(completionAuth: {success in
authorized = success
if self.authorized == true {
healthstore.getSleepMetrics(startDate: sevendaysAgo, endDate: sevendaysAfter, completion: sleepDictAssign)
}
else {
initTextContent = "Required Authorization Not Provided"
}
})
}
}
I've created a class called healthstore and am simply using HealthKit's requestAuthorization method as follows:
var healthStore: HKHealthStore()
func getHealthAuthorization(completionAuth: #escaping (Bool) -> Void) {
//we will call this inside our view
healthStore.requestAuthorization(toShare: [sleepSampleType], read: [sleepSampleType]) { (success, error) in
if let error = error {
// Handle the error here.
fatalError("*** An error occurred \(error.localizedDescription) ***")
}
else {
completion(success)
}
}
}
If the user revoked the permisions they are revoked. You cannot ask again. If you want to handle this scenario you would need to throw the error and handle it outside of the function by showing a message to the user asking them to enable it again.
Or simply return the success boolean ignoring the error.
func getHealthAuthorization(completionAuth: #escaping (Bool) -> Void) {
//we will call this inside our view
healthStore.requestAuthorization(toShare: [sleepSampleType], read: [sleepSampleType]) { (success, error) in
//ignore error since your func can just return a boolean
completion(success)
}
}

Swift application having weird behavior when closing it

I am developing an application that uses the Face/Touch ID at the opening.
I achieved this by adding this func to my MainViewController():
let context = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) {
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "Verifying") { (success, err) in
if success {
DispatchQueue.main.async {
self.loginSuccessfull()
self.button.removeFromSuperview()
}
} else {
if let err = err {
print(err)
}
}
}
}
This gets called both in the ViewDidLoad and by a button, as shown in this video.
As you can see tho, when I try to close my App it has a very weird behavior, and I am sure that it is caused by the FaceID.
Any suggestion to fix this?
Crash log:
Error Domain=com.apple.LocalAuthentication Code=-4 "Caller moved to background." UserInfo={NSLocalizedDescription=Caller moved to background.}
I believe I have found a solution for the issue, by delaying the evaluation.
I noticed that when I have any kind of delay in UI before evaluation (for example: animation that move the logo up before showing the face ID alert) the crash stops altogether.
So I did another test with delay like so:
override func viewDidAppear(_ animated: Bool) {
let context = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Biometric test") { success, error in
DispatchQueue.main.async {
if success {
doSome()
} else {
if let error = error { print(error) }
}
}
}
}
}
}
With that implementation I had zero crashes.
*Note: I also tried different delay times, from 0.1 to 2.0 seconds, all worked for me.

iOS 12 - NSFaceIDUsageDescription all of a sudden not working

I was using NSFaceIDUsageDescription in my app and it was working. I deleted my app from my device and and re-uploaded (plugging my device into my mac and running from xcode) it and now I don't get the alert that my app would like to use FaceID, how come the alert is not appearing anymore? This is preventing me from using FaceID in my app.
class TouchIDAuth {
let context = LAContext()
func canEvaluatePolicy() -> Bool {
return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
}
func authenticateUser(completion: #escaping (NSNumber?) -> Void) {
guard canEvaluatePolicy() else {
completion(0)
return
}
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Logging in with Touch ID") { (success, evaluateError) in
if success {
DispatchQueue.main.async {
completion(nil)
}
} else {
let response: NSNumber
switch evaluateError?._code {
case Int(kLAErrorAuthenticationFailed):
response = 2
case Int(kLAErrorUserCancel):
response = 3
case Int(kLAErrorUserFallback):
response = 4
default:
response = 1
}
completion(response)
}
}
}
}
And when I do this:
let touchMe = TouchIDAuth()
print(touchMe.canEvaluatePolicy())
The print returns false.
Is this an issue with my device? Or with NSFaceIDUsageDescription?
When your device exceeds the limit of incorrect attempts it usually returns false.
Try locking your device, then unlocking with face/touch ID and it starts working again in your app.
It should also return an error code why it is failling for evaluateError
Hope this was your case and solves the issue.

PFUser.becomeInBackground never returns in watchOS2

In watchOS2 I am getting the sessionToken from the iOS app with WCSession. That works great but when I try to call become in background it never returns. The console logs 1 and 2 but never makes it to print("3"). Any reason becomeInBackground wouldn't respond at all? Or is there a completely different way I should be going about this?
The sessionToken is coming from user.sessionToken in iOS.
print("1")
if let sessionToken = reply["sessionToken"] as? String {
print("2")
PFUser.becomeInBackground(sessionToken, block: { (user, error) in
print("3")
if let user = user as? User where error == nil {
print("success")
} else {
print(error)
}
})
}

swift - touchID takes long time to load

I'm working on integrating touchID into my application. The process was fairly simple, but even just using my dummy data, it takes about 5 seconds after it authenticated my fingerprint, before it performs it's task.
Here's my code:
func requestFingerprintAuthentication() {
let context = LAContext()
var authError: NSError?
let authenticationReason: String = "Login"
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &authError) {
context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: authenticationReason, reply: {
(success: Bool, error: NSError?) -> Void in
if success {
println("successfull signin with touchID")
self.emailInputField.text = "john.doe#gmail.com"
self.passwordInputField.text = "password"
self.signIn(self.signInButton)
} else {
println("Unable to Authenticate touchID")
}
})
}
}
even with the dummy data, it takes waaay too long.
When I login normally, by typing the email and the password into my inputfields, the signIn() function runs instantly.
To see if it was a problem with that. I tried replacing that, with 2 lines that simply takes me to the correct viewController. But it still takes several seconds after it's authenticated my fingerprint.
I know it's not the phone, nor touchID. Cause it runs my println("successfull signin with touchID") immediately. It's what comes after that, that for some reason takes several seconds for it to run?
Any help explaining this would be greatly appreciated!
The documentation states:
This method asynchronously evaluates an authentication policy.
You are running UI code on a thread that is not the main. Wrap your code to get it to perform on the main thread:
func requestFingerprintAuthentication() {
let context = LAContext()
var authError: NSError?
let authenticationReason: String = "Login"
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &authError) {
context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: authenticationReason, reply: {
(success: Bool, error: NSError?) -> Void in
if success {
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
println("successfull signin with touchID")
self.emailInputField.text = "john.doe#gmail.com"
self.passwordInputField.text = "password"
self.signIn(self.signInButton)
})
} else {
println("Unable to Authenticate touchID")
}
})
}
}

Resources