How to Lock your iOS app with Device Lock?
Long ago, I developed a presentation app for iPad that required a secure feature. To fulfill this requirement, I implemented a custom alert controller to prompt users for a password.
How can a user open the presentation if they forget the password?
There is no server for the app, it uses only cloud drive like Google, Dropbox, OneDrive.
Since there is no server for the app and it relies on cloud drives like Google, Dropbox, and OneDrive, I decided to utilize the device lock.
If the user can unlock the device, it signifies ownership of the device, and I don’t have to worry about potential security breaches.
How can I programmatically lock the user’s device?
LAContext
To use the lock API, I created a Local Authentication Context.
import LocalAuthentication
let context = LAContext()
Evaluate
It is very easy to lock the device; just call evaluatePolicy
.
context.evaluatePolicy(.deviceOwnerAuthentication,
localizedReason: "Please unlock") { result, error in
debugPrint("Unlocked? \(result). Error[\(error)]")
}
Policy
deviceOwnerAuthentication
is specifically designed for password authentication. However, there are 4 special policies that can be used as an alternative to the password.
Since I don’t have a watch, I attempted to use ...WithBiometrics
.
2023–12–12 18:06:15.269447+0900 iOSDeviceLockExample[17914:57394382] [access] This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSFaceIDUsageDescription key with a string value explaining to the user how the app uses this data.
But the app crashed because the description is required when using Biometrics.
When I ran it again, the app prompted me for permission.
And displayed the face detection view.
Few seconds later, the system presented the failed alert.
Since I agreed to use Face ID, I couldn’t see the password screen anymore even with .deviceOwnerAuthentication
.
Cancel
Cancel When I pressed the cancel button on the alert, the callback was invoked with an error..
Unlocked? false. Error[Optional(Error Domain=com.apple.LocalAuthentication Code=-2 \”Canceled by user.\” UserInfo={NSDebugDescription=Canceled by user., NSLocalizedDescription=Authentication canceled.})]”
2023–12–12 18:14:01.054536+0900 iOSDeviceLockExample[17989:57395550] [EventDispatcher] Found no UIEvent for backing event of type: 11; contextId: 0x209A520A
Retry
In specific cases, I need to lock again after the lock is canceled or unlocking fails. So, I called evaluatePolicy
again when the result is false.
func lock() {
let context: LAContext = .init()
context
.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Please unlock") { [weak self]result, error in
guard result else{
self?.lock()
return
}
//
}
}
lazy var onLock: UIAction = .init { [weak self]_ in
self?.lock()
}
The full source is in this example repository.
If you found this post helpful, please give it a round of applause 👏. Explore more iOS-related content in my other posts.