Ad

Our DNA is written in Swift
Jump

App-Level Passcode

In the latest version of iWoman we are finally coming around to implementing TouchID. Or more precisely: device owner authentication.

iOS 8 introduced the ability to let us use the user’s finger print for authentication. But if that wasn’t set up or otherwise unavailable, we still had to fall back to displaying a keypad via DTPinLock. iOS 9 finally gave us the ability to fall back on the device passcode, just like the user would use to unlock iPhone without TouchID.

The following figure shows the kinds of scenarios possible if the user activates iWoman’s passcode lock feature. If the app is running under iOS 9 (or higher) and if device owner authentication is possible, then we won’t need DTPinLock as fall back.

Passcode Activation

Remember: you cannot have TouchID without a passcode set. Being able to “evaluate the device owner authentication policy” means that the owner can authenticate with finger print or device passcode, but we don’t know which. We only see if authentication was successful.

The iPhone 6, introduced with iOS 8, was the first to introduce a fingerprint sensor. So TouchID is possible there, but the OS does not automatically fall back to the device PIN. This means we still have to show DTPinLock for the case that TouchID fails, or if it is not active.

We found this easy to test: all you need is to place an unregistered fingertip onto the TouchID sensor. This causes the secondary authentication button to appear, which you can freely label. We chose “Enter iWoman Passcode”. The same button also appears on iOS 9, but there we named it “Enter Device Passcode” to make it clear to the user that this is a different code.

For sake of completeness, on devices that have no TouchID support, we still use DTPinLock for the entire app authentication.

Since the app passcode is all about user privacy, we need to cover the app’s UI until the user unlocks it. For everything before iOS 9, we use DTPinLock (with optional background image) as cover. But because iOS 9 has its own passcode pad, we went with an ordinary blur view with vibrant labels.

The flow at app launch or resume is slightly more complex. I’ll break it down into two parts: blocking the app’s main view and starting authentication.

Blocking if Necessary

There are two policies which the LocalAuthentication framework can evaluate for us:

  1. DeviceOwnerAuthenticationWithBiometrics (>= iOS 8)
  2. DeviceOwnerAuthentication (>= iOS 9)

As often found in real life, longer is not necessarily better. The first one only has support for TouchID, the second one uses TouchID first and when this fails the device passcode is used as fallback.

Initially I had considered modifying DTPinLock to handle TouchID as well, but that proved to be too much hassle. So I decided on a simpler approach, to have a custom view controller with a visual effects blur view and some text explaining that the app is locked.

iWoman Modern Lock Screen

A tap gesture recognizer starts the unlocking process. I realized that this was necessary because you end up back with this block view visible if authentication fails or gets cancelled by the user.

Native Unlocking

The second part I’d rather show in Swift. At the beginning, it checks if we’re running iOS 9. If yes, then we use the better authentication policy and name the fallback button accordingly. This button appears once the fingerprint sender failed to detect a registered finger.

func startUnlocking()
{
   let context = LAContext()
   var policy: LAPolicy = .DeviceOwnerAuthenticationWithBiometrics
 
   if #available(iOS 9.0, *)
   {
      // use device passcode as fallback if available
      policy = LAPolicy.DeviceOwnerAuthentication
 
      context.localizedFallbackTitle = NSLocalizedString("ENTER_DEVICE_PASSCODE",
         comment: "Button Label for falling back to device passcode when Touch ID fails")
   }
   else
   {
      // we will be using DTPinLock as fallback, so we name it differently
      context.localizedFallbackTitle = NSLocalizedString("ENTER_APP_PASSCODE",
         comment: "Button Label for falling back to app passcode when Touch ID fails")
   }
 
   var error: NSError?
   let hasTouchID = context.canEvaluatePolicy(policy, error: &error)
 
   if hasTouchID
   {
      let reason = NSLocalizedString("TOUCH_ID_REASON",
         comment: "Label shown below 'Touch ID for iWoman' stating the reason for asking for it")
      context.evaluatePolicy(policy, localizedReason: reason) {
         success, error in
 
         dispatch_async(dispatch_get_main_queue()) {
 
            self.needsToAuthenticate = false
 
            if success
            {
               self.hideBlockingView(true)
            }
         }
      }
   }
}

Note that nothing is done in case that hasTouchID comes out as negative. The reason for this is that the blocking view is the Pin Unlock Controller from DTPinLock and so we will get a delegate callback if the user enters the correct passcode there.

In case of successful “device owner authentication” we can dismiss the blocking view. This is slightly confusing, because on iOS 8 we get into the completion handler right after TouchID failed. On iOS 9 there the fallback authentication via device passcode precedes the completion handler. Put differently: on iOS 8 failure means that you need to present your fall back mechanism, on iOS 9 failure means that both TouchID and device passcode failed.

The whole thing gets slightly more complicated by the situation that the user might enable/disable TouchID/passcode while your app is backgrounded. I have code in there as well to deal with these transitions.

Conclusion

The introduction of device owner authentication in iOS 9 (by TouchID and/or device passcode) is a boon for apps which want to prevent somebody unauthorized from gleaning infos not meant for their eyes. Before iOS 9, you could work around the missing fallback mechanism by using a commercial component like DTPinLock.

On devices before iOS 9, we had the problem that there might not be a way to get back into the app if the user forgot the app-level code. This is nicely out of our hands with iOS 9 because the user cannot “forget” fingers. If the device passcode is forgotten, then this is not our problem as app developers.

I’m glad that we invested a couple of days to finally implement the entire process, fallback and all, in iWoman. DTPinLock is available for licensing on our component store.


Categories: Recipes

3 Comments »

  1. > The iPhone 6, introduced with iOS 8, was the first to introduce a fingerprint sensor.

    Actually, The iPhone 5S, introduced with iOS 7, was the first to introduce a fingerprint sensor. However, only Apple could use it. 3rd party applications could use TouchID starting with iOS 8. The iPhone 5S can still use TouchID with 3rd party applications in IOS 9.

  2. Fingerprint sensing & touch id is new to devices which doesn’t works on android or other OS but its quite interesting and can restrict the unauthorized use thats a good sign for every use Thums Up for you