Refactoring: Face ID/Touch ID for iOS 13

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;
}
}
}

Episode 167 – Death By Design

We start this week’s show with a discussion about preordering the iPhone X. We follow up with the Quick Start method for migrating your identity to a new iOS device. We also follow up on predictions of iPhone X supply rumors. Death By Design is a documentary about the creation and disposal of all the electronic devices we crave and their toll on some societies and the environment. Jaime lightens the discussion with a look a the percentage of games using ARKit. Picks: Theodolite, iOS 11 Bug: Typing 1+2+3 Quickly in the Calculator App Won’t Get You 6, Face Tracking with ARKit.

Episode 167 Show Links:

Episode 167 Picks:

Episode 122 – What is Internet?

This week we answer #askMTJC about how we use the pragma mark in our apps. We also follow up as we test drive Setapp’s beta program. We discuss the @available attribute in Swift that allows you to use new iOS features while maintaining backward compatibility. We the new Bluetooth 5 announcement and discuss some contradictory articles on the wearables market. Aaron gives a status report on CarPlay in his new car. Picks: Pre-Apple return Steve Jobs TV interview, Wang 1980’s TV commercial and Inside the secret meeting where Apple revealed the state of its AI research.

Sponsored by: Buddy Build

Episode 122 Show Notes:

Episode 122 Picks:

Episode 79 – Your Back Door Man

This week we discuss the Letter published by Tim Cook in response to the FBI’s request to create a “back door” into iOS. We talk about Panic’s Prompt app update. We ponder Rob Rhyne’s departure to join Apple and Jaimee Newberry’s rise to COO of MartianCraft. Greg sneaks in a couple of posts on Blackfish and Swifter; two Swift based http servers. Picks: Quartz iOS App, Earl Grey and The Astonishing Powers and Mesmerizing Secrets of 3D Touch.

Episode 79 Show Notes:

Episode 79 Picks:

Episode 14 – Cracking the iOS Job Interview

This week we discuss the challenges of iOS job and contract interviews for the rest of us. We discuss common interview approaches, how and why the don’t work. We also revisit the age old native mobile app versus web apps – is it still a relevant approach in execution and cross browser use. We discuss Ben Thompson’s post on how Apple leverages its market place dominance as the world moves toward Apple Pay. This weeks picks is an Apple Watch Simulator – Happy Watch

Episode 14 – Show Notes

Cracking the Code Interview Programming Questions – Gayle Laakmann McDowell
Lukas Mathis on Web vs Native Apps
Apple’s leverage with Apple Pay

Pick

Watch Simulator – Happy Watch

sky deck
sky deck

Listen or Subscribe on iTunes

Listen or Subscribe on FeedBurner

Xcode Process launch failed: Security

launch failed security

The latest annoyance in Xcode had me stumped for a bit but @smappsoft set me straight. When trying to build and app under iOS 8, the build succeeds but the app doesn’t launch. Saying “process launch failed: Security” and not much else.

Turns out that the build was successful but your shiny new iOS 8 device doesn’t “trust” your profile.

Security: 1, Developer: 0

IMG_5124-cropped

So to solve this new cryptic behavior, launch the app on your device (which you just installed) and you will get a trust prompt asking you to approve an “Untrusted App Developer”. This is Gatekeeper asking you to confirm your trust of the developer (yourself).

Once you’ve completed this trust you can happily install apps from Xcode.

MTJC podcast – Ep 7 – Wait! What? How much?

Episode #7 – Wait! What? How Much – Sept 17, 2014 

In the 7th episode we discuss:

  • John Gruber’s hypothetical Apple Watch pricing
  • Upgrading to iOS 8 and caution over updating to iCloudDrive
  • Experiences around pre-ordering iPhone 6 and which models we each chose and why
  • Mark Rubin Phd surprises some of us with his knowledge of integrated circuit manufacture.

 

Episode 7 Show Notes:
Apple Watch: Initial Thoughts and Observations
Intel: Apple’s ‘A8′ Chip Exposes Shortcomings, Says Semi Advisors
Only Stephen Fry can make a keyboard sound profoundly life-changing
Orchard app – sell your iPhone
Panic Transmit for iOS
The Vector Project

New iPhone 6 screen shots for ITC

I can here you say, “Everybody knows this already.” However for those of you who don’t (non developers, artists maybe) there are the sizes needed to create new screen shots for iTunesConnect app metadata and marketing. Apple hasn’t produced an iOS 8/iPhone 6 transition guide and the Mobile HIG hasn’t been updated, yet.

iPhone 6 – 755 x 1334

iPhone 6 Plus – 1242 x 2208

The iPhone 6 Plus also has landscape mode so you may want to create screen shots for that mode as well.