iOS17下Swift LocalAuthentication中FaceID重置State变量的问题
iOS17中FaceID回调无法修改SwiftUI @State变量的问题
我尝试通过FaceID验证来修改SwiftUI里的@State布尔变量showPassword,该功能在iOS16上运行正常,但升级到iOS17后出现异常:在点击手势里先设置showPassword=true,当FaceID验证成功触发回调时,这个变量会被重置为false,即使在回调里执行showPassword=true也无法生效。
原始测试代码
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: 30, height: 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 { // 无FaceID/生物识别功能 } } } } }
出现异常的补充代码片段
.onTapGesture { showPassword=true // 添加此行代码 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 { // showPassword会被重置为false print("SUCCESS") showPassword=true // 此行代码无法将变量设为true } else { print("FAIL") showPassword=false } } } else { // 无FaceID/生物识别功能 } }
解决方案
问题核心在于LAContext.evaluatePolicy的回调是在后台线程执行的,而SwiftUI的@State变量必须在主线程中修改才能正确触发视图更新。iOS17对线程检查更加严格,导致之前iOS16中可能侥幸生效的代码现在失效。
只需要将回调中修改showPassword的代码包裹在DispatchQueue.main.async中,确保在主线程执行:
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in DispatchQueue.main.async { // 切换到主线程更新状态 if success { print("SUCCESS") showPassword=true } else { print("FAIL") showPassword=false } } }
另外可以简化图片的frame设置,因为宽高值固定,无需根据showPassword判断:
.frame(width: 30, height: 39)
内容的提问来源于stack exchange,提问作者neogaz
相关产品推荐
相关产品推荐

