Swift中变量赋值后为何变回nil?Firebase登录场景问题排查
Firebase邮箱/密码登录的账号激活验证失效问题
问题详情
用Firebase实现邮箱/密码注册登录流程:
- 用户填写表单注册,完成Firebase邮箱验证后,等待管理员手动激活账号
- 注册时会在
Users集合创建新文档,存入用户信息,默认将IsActiveUser设为false - 登录时需要做两项检查:
- 验证
Auth.auth().currentUser?.isEmailVerified为true(这部分正常工作) - 从
Users集合获取用户信息,检查isActiveUser字段是否为true(这部分失效)
- 验证
- 调试时发现:
docRef.getDocument闭包内变量能正确赋值,但后续验证语句执行前isActive又变回nil
原代码
struct LoginView:View { @State private var isActive: Bool? Button("Login", action: { Auth.auth().signIn(withEmail: userEmail, password: userPass) { authresult, error in if let e = error { //display error } else { // User has correct user/pass let userID = Auth.auth().currentUser?.uid let docRef = db.collection("Users").document(userID!) docRef.getDocument { (document, error) in if let document = document, document.exists { print(document.data()!) isActive = document.data()!["isActiveUser"]! as? Bool print("Inside if/let: \(isActive)") //this seems to work, and pulls the correct information } else { print("Document does not exist") } } print("Before validation \(isActive)") //here it prints nil if Auth.auth().currentUser?.isEmailVerified == false { // Email address needs to be verified. Alert user and signs out. This works. } else if isActive == false { // Account has not been activated by administrator. Alert user and signs out. This does not work. } else { // User is successfully logged in. } } } }).buttonStyle(BrandButtonStyle()) .alert(isPresented: $showingAlert) { Alert(title: Text(errorTitle), message: Text(errorText), dismissButton: .default(Text("OK"))) } }
调试输出
Before validation nil ["userEmail": foo@bar.com, "userID": moAyFkkhd9gXDpnRZwadHc5rtL22, "isActiveUser": 0, "userName": Foo Bar] Inside if/let: Optional(false)
问题原因
docRef.getDocument是异步网络请求,代码执行顺序不是从上到下同步的:
- 调用
getDocument后,立即继续执行后面的print和验证逻辑,此时isActive还是初始的nil - 等Firebase服务器返回用户数据后,才会进入闭包给
isActive赋值,但这时候验证逻辑已经跑完了
修复方案
把所有依赖isActive的验证逻辑移到getDocument的闭包内部,确保拿到服务器返回的数据后再执行判断。同时优化代码,避免强制解包导致崩溃:
struct LoginView:View { @State private var isActive: Bool? @State private var showingAlert = false @State private var errorTitle = "" @State private var errorText = "" // 假设已定义userEmail、userPass、db等变量 var body: some View { Button("Login", action: { Auth.auth().signIn(withEmail: userEmail, password: userPass) { authresult, error in if let e = error { errorTitle = "登录失败" errorText = e.localizedDescription showingAlert = true return } guard let currentUser = Auth.auth().currentUser else { errorTitle = "错误" errorText = "无法获取当前用户信息" showingAlert = true return } // 先检查邮箱是否验证,减少不必要的数据库请求 if !currentUser.isEmailVerified { errorTitle = "邮箱未验证" errorText = "请先验证您的邮箱地址,验证完成后再登录" showingAlert = true try? Auth.auth().signOut() return } // 获取用户文档检查激活状态 let docRef = db.collection("Users").document(currentUser.uid) docRef.getDocument { (document, error) in if let error = error { errorTitle = "获取用户信息失败" errorText = error.localizedDescription showingAlert = true try? Auth.auth().signOut() return } guard let document = document, document.exists, let isActiveUser = document.data()?["isActiveUser"] as? Bool else { errorTitle = "用户信息异常" errorText = "无法找到您的账号信息,请联系管理员" showingAlert = true try? Auth.auth().signOut() return } self.isActive = isActiveUser print("获取到激活状态: \(self.isActive)") if !isActiveUser { errorTitle = "账号未激活" errorText = "您的账号尚未被管理员激活,请等待激活后再登录" showingAlert = true try? Auth.auth().signOut() } else { // 登录成功,执行后续逻辑(比如跳转主页) print("登录成功") } } } }).buttonStyle(BrandButtonStyle()) .alert(isPresented: $showingAlert) { Alert(title: Text(errorTitle), message: Text(errorText), dismissButton: .default(Text("OK"))) } } }
额外优化说明
- 用
guard let替代强制解包(!),避免空值导致的崩溃 - 提前检查邮箱验证状态,减少不必要的Firebase数据库请求
- 统一错误处理逻辑,给用户明确的错误提示
- 操作失败时主动登出用户,避免用户处于异常登录状态
内容的提问来源于stack exchange,提问作者stygarfield
相关产品推荐
相关产品推荐

