You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI Firebase Auth错误处理首次点击不触发弹窗问题

问题现象

接入 Firebase Auth 实现注册登录功能时,使用无效凭证首次点击「注册/登录」按钮不会弹出错误警告,必须第二次点击按钮才能正常触发错误弹窗,注册、登录模块均存在该问题。

相关实现代码

AuthViewModel 核心代码

@Published var userSession: User?
@Published var currentUser: AppUser?
@Published var signupError: Error?
@Published var loginError: Error?

static let shared = AuthViewModel()

init() {
    self.userSession = Auth.auth().currentUser
    fetchUser()
}

func fetchUser() {
    guard let uid = userSession?.uid else { return }
    USER_COLLECTION.document(uid).getDocument { snapshot, _ in
        guard let user = try? snapshot?.data(as: AppUser.self) else { return }
        self.currentUser = user
    }
}


func registerUser(withEmail email: String, password: String) {
    Auth.auth().createUser(withEmail: email, password: password) { result, error in
        if let error = error {
            DispatchQueue.main.async {
                self.signupError = error
                print("error")
                return
            }
        }

        guard let user = result?.user else { return }
        let data: [String: Any] = ["uid": user.uid, "email": user.email ?? ""]
        USER_COLLECTION.document(user.uid).setData(data) { err in
            self.userSession = user
            self.fetchUser()
        }
    }
}


func login(withEmail email: String, password: String) {
    Auth.auth().signIn(withEmail: email, password: password) { result, error in
        if let error = error {
            DispatchQueue.main.async {
                self.loginError = error
                return
            }
        }

        guard let user = result?.user else { return }
        self.userSession = user
        self.fetchUser()
    }
}

注册视图按钮实现

Button {
    authViewModel.registerUser(withEmail: email, password: password)
                       
    print("called")
    if let error = authViewModel.signupError {
        alertTitle = "注册失败"
        alertMessage = error.localizedDescription
        alertShowing = true
        print(alertShowing)
    }
    print(alertShowing) 
} label: {
    CustomAuthButton(text: "注册")
}

弹窗挂载代码

.alert(alertTitle, isPresented: $alertShowing) {
    Button("确定") {
        authViewModel.signupError = nil
    }
} message: {
    Text(alertMessage)
} 

控制台日志输出

called
false
2022-07-06 12:59:54.424510+0530 InfoMax[62528:2751506] [boringssl] boringssl_metrics_log_metric_block_invoke(153) Failed to log metrics
error
问题原因

Firebase 的 createUser、signIn 方法均为异步执行的网络请求:点击按钮调用注册方法后,代码会立刻同步执行后续的错误检查逻辑,此时网络请求尚未返回,signupError 仍为空值,不会触发弹窗。等网络请求返回错误、完成signupError赋值时,按钮闭包内的检查逻辑早已执行完毕。第二次点击按钮时,上一次请求留存的错误值已存在,同步检查时可以拿到错误内容,才会触发弹窗。日志中 boringssl 相关输出为Xcode系统无关日志,不影响功能逻辑。

修复方案

不要在按钮点击的同步闭包里手动检查错误值,直接用 SwiftUI 的 onChange 修饰符监听 signupError 的变化,错误值更新时自动触发弹窗即可,修改逻辑如下:

  • 移除按钮闭包里的错误检查逻辑,按钮只保留调用注册方法的代码:
Button {
    authViewModel.registerUser(withEmail: email, password: password)
} label: {
    CustomAuthButton(text: "注册")
}
  • 给视图添加 onChange 修饰符监听错误状态变化,保留原有弹窗逻辑:
.onChange(of: authViewModel.signupError) { newError in
    guard let error = newError else { return }
    alertTitle = "注册失败"
    alertMessage = error.localizedDescription
    alertShowing = true
}
.alert(alertTitle, isPresented: $alertShowing) {
    Button("确定") {
        authViewModel.signupError = nil
    }
} message: {
    Text(alertMessage)
}

注意:所有 @Published 状态属性的更新必须在主线程执行,之前添加的 DispatchQueue.main.async 包裹错误赋值的逻辑需要保留,避免 SwiftUI 状态更新不在主线程导致的页面渲染异常。
登录模块的同类问题处理逻辑完全一致,监听loginError的变化触发登录失败弹窗即可。

内容的提问来源于stack exchange,提问作者Aakash Kothari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:39:15