Refactoring: Face ID/Touch ID for iOS 13 Update

Back in February 2015, my article on Touch ID was published on raywenderlich.com. I was written in Swift for Xcode 8. Every year or so i would update the article as an author on the iOS Team. Here’s a link to the latest — 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. My app was also still supporting Objective-C so 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;
}
}
}

Episode 197 – Dub Dub San Jose: WWDC Crystal Ball

We are joined by Greg Heo as we look ahead and suggest what Apple may introduce at WWDC 2018. We follow up on the Command key icon, castle connection, as well as Clifford Stoll and the Cockoo’s Egg. Jaime uncovers ⌘+Ctrl+Shift+H to find a methods callers. Tim relays early experience drawing and designing typefaces in the early days of Mac. AirPlay 2 gives the HomePod the ability to have multi-room and stereo audio. HomePod launching in Canada, France, and Germany. Felix Krause thinks apps should be in a network sandbox. App Insight attempts to expose 3rd party framework use. Genius Bar Broke an iMac Pro. We lay down our best guesses about where Apple will go with WWDC 2018. Picks: How to debug your mobile hybrid app on iOS, Beacon – Signal Availability by Ashley Nelson-Hornstein, Prioritize Work with Quality of Service Classes.

Episode 197 Show Links

Episode 197 Picks

Episode 193 – Mind Altering Software Development

We start off with some #askMTJC about Property Animators, the possibly faked iPad 2018 armor, and dealing with corrupt and challenging iOS backup and restore. On this mostly follow up show, we talk about our space grey 2018 T-Shirt, Swift for TensorFlow is now available, cross-platform Apple development probably not coming at WWDC, JAMF stats on platform choices in the enterprise and benchmarking Codable. We also discuss Apple’s earnings and some history via Bill Atkinson’s new post to folklore.org. Congratulations to our book winner: Adam Armstrong. iTunes is now on the Microsoft Store. Apple’s new MacBook Air is delayed until late 2018. Picks: What’s New In Swift, Licenses + Plain English, Classic Computer Science Problems in Swift: Essential Techniques for Practicing Programmers

Episode 193 Show Links:

Episode 193 Picks:

Episode 191 – Functional Brain Hacking

We follow up on Xcode Swift build times, Apple cracks down on drone flyovers at Apple Park and alternative music services that provide Malayalam, Hindi, Tamil and Bollywood tunes. Apple will replace Watch 2 devices with swollen batteries. What’s new in Swift 4.2? How to name UIButton actions. We discuss whether Apple has a problem fitting in features on the iPhone. Picks: Personalized Hey Siri, Introducing Flight School: a new book series for Swift developers by @mattt, How To Keep Learning After 50 and Other Ways to Hack Your Brain at any Age, Privacy Essentials Safari Extension

Tim & Tammy Brain Hacking Photo: Greg Heo

Episode 191 Show Links:

Episode 191 Picks: