I see "Unlocked" text when I try Face ID once again. How to display empty screen or Splash Screen instead of it?
My code is the following:
import Foundation
import LocalAuthentication
class UserAuthentification{
func authenticate(){
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else {
print("No Biometric Sensor Has Been Detected. This device does not support FaceID/TouchID.")
return
}
context.evaluatePolicy(LAPolicy.deviceOwnerAuthentication, localizedReason: "Only device owner is allowed", reply: { (success, error) -> Void in
if( success ) {
print("FaceID/TouchID. You are a device owner!")
} else {
// Check if there is an error
if let errorObj = error {
print("Error took place. \(errorObj.localizedDescription)")
}
if LAError.Code.userCancel.rawValue == -2{
self.authenticate()
}
}
})
}
}
struct ContentView: View {
var variable : UserAuthentification
var body: some View {
// This text I see after retrying FaceID
Text("Unlocked")
.onAppear(perform: variable.authenticate)
}
}
Thanks for answering in Advance!)
I would modify your UserAuthentication
class so that it exposes an @Observable
object to indicate the authenticated state.
You also have a mistake in your check for a user cancelled result - You are simply checking the constant's own value. You need to check the code
in the LAError
import Foundation
import LocalAuthentication
class UserAuthenticator: ObservableObject {
@Published private(set) var isAuthenticated = false
@Published private(set) var error: Error?
func authenticate(){
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
print("No Biometric Sensor Has Been Detected. This device does not support FaceID/TouchID.")
return
}
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Only device owner is allowed", reply: { (success, error) -> Void in
DispatchQueue.main.async {
self.error = error
if( success ) {
print("FaceID/TouchID. You are a device owner!")
self.isAuthenticated = true
} else {
// Check if there is an error
if let errorObj = error as? LAError {
print("Error took place. \(errorObj.localizedDescription)")
if errorObj.code == .userCancel {
self.authenticate()
}
}
}
}
})
}
func deauthenticate() {
self.isAuthenticated = false
}
}
You can then inject an instance of this class as a model object into your view and use the isAuthenticated
property to control the view state:
import SwiftUI
struct ContentView: View {
@ObservedObject var authenticator: UserAuthenticator
var body: some View {
HStack {
if authenticator.isAuthenticated {
VStack {
Text("Welcome")
Button(action: {self.authenticator.deauthenticate()}) {
Text("Logout")
}
}
} else {
VStack {
Text("Please authenticate").onAppear {
self.authenticator.authenticate()
}
if self.authenticator.error != nil {
Text(self.authenticator.error!.localizedDescription)
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(authenticator: UserAuthenticator())
}
}
You will need to inject the UserAuthenticator
instance from your scene delegate just as I have done in the preview provider code.
This is a bare-bones example; you need to consider re-try limits and fallback if biometric authentication isn't available, but this should get you started.