I'm trying to change a state bool var with faceID.
My Test Code.
import SwiftUI
import LocalAuthentication
struct PasswordEditTest: View {
@State private var showPassword: Bool = false
var body: some View {
HStack {
Section(header: Text("Password:").foregroundColor(Color.theme.red)
) {
if showPassword {
Text("UNLOCKED")
}
else {
Text("LOCKED")
}
}
Spacer()
Image(systemName: showPassword ? "lock.open" : "lock")
.resizable()
.scaledToFill()
.frame(width: showPassword ? 30 : 30, height: showPassword ? 39 : 39)
.padding()
.onTapGesture {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "We need to unlock your password"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
if success {
print("SUCCESS")
showPassword=true
} else {
print("FAIL")
showPassword=false
}
}
} else {
//NO FACEID/Biometrics
}
}
}
}
}
If I change the showPassword to TRUE on the tap gesture, once the faceid success fires it will change the var back to false, So strange. It was working fine on iOS16
.onTapGesture {
showPassword=true //ADD THIS
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "We need to unlock your password"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
if success {
// the showPassword will change back to false.
print("SUCCESS")
showPassword=true. // This will not change it to true Crazy!
} else {
print("FAIL")
showPassword=false
}
}
} else {
//NO FACEID/Biometrics
}
You can switch to the async/await
version, it will ensure that the result is received in the MainActor
import SwiftUI
import LocalAuthentication
struct PasswordResetTest: View {
@State private var showPassword: Bool = false
@State private var isEvaluatingPolicy: Bool = false
var body: some View {
HStack {
Section(header: Text("Password:").foregroundColor(Color.red)
) {
if showPassword {
Text("UNLOCKED")
}
else {
Text("LOCKED")
}
}
Spacer()
Image(systemName: showPassword ? "lock.open" : "lock")
.resizable()
.scaledToFill()
.frame(width: showPassword ? 30 : 30, height: showPassword ? 39 : 39)
.padding()
.onTapGesture {
//Start evaluating policy
isEvaluatingPolicy.toggle()
}
.task(id: isEvaluatingPolicy) {
guard isEvaluatingPolicy else {return}
let context = LAContext()
let policy: LAPolicy = .deviceOwnerAuthenticationWithBiometrics
do {
try context
.canEvaluatePolicy(policy)
showPassword = try await context
.evaluatePolicy(policy, localizedReason: "We need to unlock your password")
print("Was successful = \(showPassword.description)")
} catch {
print(error) //Should display error to user.
}
isEvaluatingPolicy = false //Done evaluating policy
}
}
}
}
The referenced canEvaluatePolicy
is a simple conversion of the existing methodology.
extension LAContext {
/// Assesses whether authentication can proceed for a given policy.
/// - Parameter policy: The policy to evaluate. For possible values,
func canEvaluatePolicy(_ policy: LAPolicy) throws {
var error: NSError?
if self
.canEvaluatePolicy(policy, error: &error) {
//Done
} else if let error {
throw error
} else {
throw LocalError.cannotBeEvaluated(policy)
}
}
private enum LocalError: LocalizedError {
case cannotBeEvaluated(LAPolicy)
var errorDescription: String?{
switch self {
case .cannotBeEvaluated(let lAPolicy):
return "Policy cannot be evaluated."
}
}
}
}