Back in February 2015, my article on Touch ID was published on raywenderlich.com. It was written in Swift for Xcode 8. Every year or so, I would update the article while I was an author on the iOS Team. Here’s a link to the latest version — How To Secure iOS User Data: The Keychain and Biometrics – Face ID or Touch ID. A few months ago, I had to update one of my own apps for iOS 13 with Apple’s biometric identification framework, Local Authentication. Incidentally, my app was also still supporting Objective-C. Here’s follow up on what I had to change. As a bonus you can also take your user to Settings in case they have disabled
First thing is to add Local Authentication at the top of the Login view controller.
#import <LocalAuthentication/LocalAuthentication.h>
Next create an action for the Touch ID method:
– (IBAction)touchIDAction:(id)sender {
LAContext myContext = [[LAContext alloc] init];
NSError authError = nil;
NSString *myLocalizedReasonString = @”Used for quick and secure access to the test app”;
//…
}
After that we need to check if the device can support biometrics with canEvaluatePolicy and have an error ready.
Inside the touchIDAction add:
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
error:&authError]) {
// 1. successful steps
} else {
// 2. Oops. There's a error!
}
Inside the canEvaluatePolicy, we’ll use evaluatePolicy:localizedReason:reply. The reply will have a block that either succeeds or fails with our error.
// 1. successful steps.
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(**BOOL** success, NSError *error) {
**if** (success) {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(**void**){
*//Background Thread*
dispatch_async(dispatch_get_main_queue(), ^(**void**){
*//Run UI Updates*
// using a Keychain utility method to get the email and password
NSString *passwordFound = [KeychainUtils getPasswordForUsername:**self**->emailTextField.text andServiceName:@"My_app" error:**nil**];
**self**->passwordTextField.text = passwordFound;
**self**->usingSecureID = **true**; // a Bool I added to keep track
[**self** loginAction:**nil**];
[NSLog showWithStatus:@"Logging_In"];
});
});
} **else** {
*// User did not authenticate successfully, look at error and take appropriate action*
//I'm using a showAlert method to bring up a UIAlertViewController
[**self** showAlert: @"There was a problem verifying your identity." withTitle:@"Error!"];
**return**;
}
}];
What do we do if there is an error enabling Face ID/Touch ID? It could be because the user has disabled the feature. What’s new is that we can now take the user to your application settings — without a hack.
Initially you can pop up an alert to inform the user. Added to UIKit in iOS 8, UIApplicationOpenSettingsURLString lets you add a button to the alert that will take the user to your app in Settings, where they can enable Face ID/Touch ID.
*// Could not evaluate policy; look at authError and present an appropriate message to user*
NSString *title = @"Error!";
NSString *message = @"Your device cannot authenticate using TouchID.";
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
*// do we need to return animation?*
}];
*// open your app in Settings*
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
UIApplication *application = [UIApplication sharedApplication];
NSString *settingTitle = @"Settings";
UIAlertAction* settingsAction = [UIAlertAction actionWithTitle:settingTitle style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[application openURL:url options:@{}
completionHandler:**nil**];
}];
[alert addAction:settingsAction];
[alert addAction:defaultAction];
[**self** presentViewController:alert animated:**YES** completion:**nil**];
**return**;
}
The whole method would look like this:
- (**IBAction**)touchIDAction:(**id**)sender {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = **nil**;
NSString *myLocalizedReasonString = @"Used for quick and secure access to the test app";
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
// 1. successful steps
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(**BOOL** success, NSError *error) {
**if** (success) {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(**void**){
*//Background Thread*
dispatch_async(dispatch_get_main_queue(), ^(**void**){
*//Run UI Updates*
// using a Keychain utility method to get the email and password
NSString *passwordFound = [KeychainUtils getPasswordForUsername:**self**->emailTextField.text andServiceName:@"My_app" error:**nil**];
**self**->passwordTextField.text = passwordFound;
**self**->usingSecureID = **true**; // a Bool I added to keep track
[**self** loginAction:**nil**];
[NSLog showWithStatus:@"Logging_In"];
});
});
} **else** {
*// User did not authenticate successfully, look at error and take appropriate action*
//I'm using a showAlert method to bring up a UIAlertViewController
[**self** showAlert: @"There was a problem verifying your identity." withTitle:@"Error!"];
**return**;
}
}];
} else {
// 2. Oops. There's a error!
*// Could not evaluate policy; look at authError and present an appropriate message to user*
NSString *title = @"Error!";
NSString *message = @"Your device cannot authenticate using TouchID.";
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
*// do we need to return animation?*
}];
*// open your app in Settings*
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
UIApplication *application = [UIApplication sharedApplication];
NSString *settingTitle = @"Settings";
UIAlertAction* settingsAction = [UIAlertAction actionWithTitle:settingTitle style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[application openURL:url options:@{}
completionHandler:**nil**];
}];
[alert addAction:settingsAction];
[alert addAction:defaultAction];
[**self** presentViewController:alert animated:**YES** completion:**nil**];
**return**;
}
}
}
The post Refactoring: Face ID/Touch ID for iOS 13 appeared first on iT Guy Technologies.