The vast majority of Apple’s devices come with biometric authentication as standard, which means they use fingerprint and facial recognition to unlock. This functionality is available to us too, which means we can make sure that sensitive data can only be read when unlocked by a valid user.
This is another Objective-C API, but it’s only a little bit unpleasant to use with SwiftUI, which is better than we’ve had with some other frameworks we’ve looked at so far.
Before we write any code, you need to add a new key to your Info.plist file, explaining to the user why you want access to Face ID. For reasons known only to Apple, we pass the Touch ID request reason in code, and the Face ID request reason in Info.plist.
Open Info.plist now, right-click on some space, then choose Add Row. Scroll through the list of keys until you find “Privacy - Face ID Usage Description” and give it the value “We need to unlock your data.”
Now head back to ContentView.swift, and add this import near the top of the file:
import LocalAuthentication
OK, we’re all set to use biometrics. I mentioned earlier this was “only a little bit unpleasant”, and here’s where it comes in: Swift developers use the Error
protocol for representing errors that occur at runtime, but Objective-C uses a special class called NSError
. Because this is an Objective-C API we need to use NSError
to handle problems, and pass it using &
like a regular inout
parameter.
We’re going to write an authenticate()
method that isolates all the biometric functionality in a single place. To make that happen requires four steps:
LAContext
, which allows us to query biometric status and perform the authentication check.Please go ahead and add this method to ContentView
:
func authenticate() {
let context = LAContext()
var error: NSError?
// check whether biometric authentication is possible
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
// it's possible, so go ahead and use it
let reason = "We need to unlock your data."
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
// authentication has now completed
DispatchQueue.main.async {
if success {
// authenticated successfully
} else {
// there was a problem
}
}
}
} else {
// no biometrics
}
}
That method by itself won’t do anything, because it’s not connected to SwiftUI at all. To fix that we need to do add some state we can adjust when authentication is successful, and also an onAppear()
modifier to trigger authentication.
So, first add this property to ContentView
:
@State private var isUnlocked = false
That simple Boolean will store whether the app is showing its protected data or not, so we’ll flip that to true when authentication succeeds. Replace the // authenticated successfully
comment with this:
self.isUnlocked = true
Finally, we can show the current authentication state and begin the authentication process inside the body
property, like this:
VStack {
if self.isUnlocked {
Text("Unlocked")
} else {
Text("Locked")
}
}
.onAppear(perform: authenticate)
If you run the app there’s a good chance you just see “Locked” and nothing else. This is because the simulator isn’t opted in to biometrics by default, and we didn’t provide any error messages, so it fails silently.
To take Face ID for a test drive, go to the Hardware menu and choose Face ID > Enrolled, then launch the app again. This time you should see the Face ID prompt appear, and you can trigger successful or failed authentication by going back to the Hardware menu and choosing Face ID > Matching Face or Non-matching Face.
All being well you should see the Face ID prompt go away, and underneath it will be the “Unlocked” text view – our app has detected the authentication, and is now open to use.
SPONSORED From January 26th to 31st you can join a FREE crash course for iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a senior developer!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.