NEW: Join my free 100 Days of SwiftUI challenge today! >>

Locking our UI behind Face ID

Paul Hudson    @twostraws   

To finish off our app, we’re going to make one last important change: we’re going to require the user to authenticate themselves using either Touch ID or Face ID in order to see all the places they have marked on the app. After all, this is their private data and we should be respectful of that, and of course it gives me a chance to let you use an important skill in a practical context!

First we need some new state in ContentView that tracks whether the app is unlocked or not. So, start by adding this new property:

@State private var isUnlocked = false

Second, we need to add the “Privacy - Face ID Usage Description” key to Info.plist, explaining to the user why we want to use Face ID. You can enter what you like, but “Please authenticate yourself to unlock your places” seems like a good choice.

Third, we need to add import LocalAuthentication to the top of ContentView.swift, so we have access to Apple’s authentication framework.

And now for the hard part. If you recall, the code for biometric authentication was a teensy bit unpleasant because of its Objective-C roots, so it’s always a good idea to get it far away from the neatness of SwiftUI. So, we’re going to write a dedicated authenticate() method that handles all the biometric work:

  1. Creating an LAContext so we have something that can check and perform biometric authentication.
  2. Ask it whether the current device is capable of biometric authentication.
  3. If it is, start the request and provide a closure to run when it completes.
  4. When the request finishes, push our work back to the main thread and check the result.
  5. If it was successful, we’ll set isUnlocked to true so we can run our app as normal.

Add this method to ContentView now:

func authenticate() {
    let context = LAContext()
    var error: NSError?

    if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
        let reason = "Please authenticate yourself to unlock your places."

        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in

            DispatchQueue.main.async {
                if success {
                    self.isUnlocked = true
                } else {
                    // error
                }
            }
        }
    } else {
        // no biometrics
    }
}

Remember, the string in our code is used for Touch ID, whereas the string in Info.plist is used for Face ID.

And now we need to make an adjustment that is in reality very small, but can be hard to visualize if you’re reading this rather than watching the video. Everything inside the ZStack needs to be indented in by one level, and have this placed before it:

if isUnlocked {

Just before the end of the ZStack add this:

} else {
    // button here
}

So, it should look something like this:

ZStack {
    if isUnlocked {
        MapView…
        Circle…
        VStack…            }
    } else {
        // button here
    }
}
.alert(isPresented: $showingPlaceDetails) {

So now we all we need to do is fill in the // button here comment with an actual button that triggers the authenticate() method. You can design whatever you want, but something like this ought to be enough:

Button("Unlock Places") {
    self.authenticate()
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.clipShape(Capsule())

You can now go ahead and run the app again, because our code is done. If this is the first time you’ve used Face ID in the simulator you’ll need to go to the Hardware menu and choose Face ID > Enrolled, but once you relaunch the app you can authenticate using Hardware > Face ID > Matching Face.

That’s another app done – good job!

LEARN SWIFTUI FOR FREE I have a massive, free SwiftUI video collection on YouTube teaching you how to build complete apps with SwiftUI – check it out!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5