I have an app written in Swift that uses LocalAuthentication to allow users to log-in to the app with TouchID or phone passcode. I have set a notification observer to launch authentication block whenever app comes to Foreground so the user can re-authenticate. Everything works great and TouchID alert is displayed when the app comes to foreground after I press the home button and send the app to the background, but the problem arises after I navigate to another app from my app (such as any other app notification appears at the top and it's clicked to navigate to it) and then want to return to my app:
When my app returns to foreground, TouchID alert is not displayed as it should. Instead, my app looks as if it's not authenticated (empty tableView) and after I lock (deauthenticate) and unlock (authenticate), in order to force authentication, I get series of alerts stating "Canceled by another authentication" and at least 2 TouchID alerts.
It seams to me that the TouchID alert is being fired, but remains hidden and is then canceled by lock/unlock action to force another one.
Is there anyone that had similar problem?
Here is some code related to this problem:
override func viewDidLoad() {
super.viewDidLoad()
defaults.set(false, forKey: "authenticated")
NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationWillEnterForeground, object: nil, queue: nil, using: {_ in
self.navigationController?.popToRootViewController(animated: true)
self.accounts = []
self.tableView.reloadData()
self.authenticateUser()
self.defaults.set(false, forKey: "authenticated")
})}
func authenticateUser() {
let context = LAContext()
var error: NSError?
let reason = "Identify yourself"
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
print("canEvaluateWithTouchID")
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason:
reason, reply: {success, error in
print("EvaluateWithTouchID")
// Touch ID
DispatchQueue.main.async {
if success {
self.navigationItem.rightBarButtonItem?.isEnabled = true
self.searchBar.isUserInteractionEnabled = true
self.fetchFromCoreData()
self.defaults.set(true, forKey: "authenticated")
print("Success: TouchID")
} else {
self.defaults.set(false, forKey: "authenticated")
self.accounts = []
self.navigationItem.rightBarButtonItem?.isEnabled = false
self.searchBar.isUserInteractionEnabled = false
self.lockButton.title = "Unlock"
self.tableView.reloadData()
switch error!._code {
case Int(kLAErrorAuthenticationFailed):
self.loginAlert(message: error!.localizedDescription)
print("AuthFailed1")
case Int(kLAErrorUserCancel):
self.loginAlert(message: error!.localizedDescription)
print("UserCanceled1")
case Int(kLAErrorBiometryNotEnrolled):
self.loginAlert(message: error!.localizedDescription)
print("biometry1")
case Int(kLAErrorPasscodeNotSet):
self.userFallbackPasswordAlertWith(error: error!)
print("PassNotSet1")
case Int(kLAErrorSystemCancel):
self.loginAlert(message: error!.localizedDescription)
print("SystemCancel1")
case Int(kLAErrorUserFallback):
self.userFallbackPasswordAlertWith(error: error!)
print("UserFallback1")
default:
self.userFallbackPasswordAlertWith(error: error!)
print("default1")
}
}
}
})
} else {
print("canEvaluateWithPasscode")
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason, reply: {success, error in
print("EvaluateWithPasscode")
//No Touch ID
DispatchQueue.main.async {
if success {
self.navigationItem.rightBarButtonItem?.isEnabled = true
self.searchBar.isUserInteractionEnabled = true
self.lockButton.title = "Lock"
self.fetchFromCoreData()
self.defaults.set(true, forKey: "authenticated")
} else {
self.defaults.set(false, forKey: "authenticated")
self.accounts = []
self.navigationItem.rightBarButtonItem?.isEnabled = false
self.searchBar.isUserInteractionEnabled = false
self.lockButton.title = "Unlock"
self.tableView.reloadData()
switch error!._code{
case Int(kLAErrorAuthenticationFailed):
self.loginAlert(message: error!.localizedDescription)
print("AuthFailed2")
case Int(kLAErrorUserCancel):
self.loginAlert(message: error!.localizedDescription)
print("UserCanceled2")
case Int(kLAErrorBiometryNotEnrolled):
self.loginAlert(message: error!.localizedDescription)
print("biometry2")
case Int(kLAErrorPasscodeNotSet):
self.userFallbackPasswordAlertWith(error: error!)
print("PassNotSet2")
case Int(kLAErrorSystemCancel):
self.loginAlert(message: error!.localizedDescription)
print("SystemCancel2")
case Int(kLAErrorUserFallback):
self.userFallbackPasswordAlertWith(error: error!)
print("UserFallback2")
default:
self.userFallbackPasswordAlertWith(error: error!)
print("default2")
}
}
}
})
}
}
@IBAction func lockButton(_ sender: UIBarButtonItem) {
accounts = []
tableView.reloadData()
if lockButton.title == "Unlock" {
authenticateUser()
}
if lockButton.title == "Lock" {
lockButton.title = "Unlock"
}
defaults.set(false, forKey: "authenticated")
}
Any input would be appreciated. Thanks.