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

Swift中变量赋值后为何变回nil?Firebase登录场景问题排查

Firebase邮箱/密码登录的账号激活验证失效问题

问题详情

用Firebase实现邮箱/密码注册登录流程:

  • 用户填写表单注册,完成Firebase邮箱验证后,等待管理员手动激活账号
  • 注册时会在Users集合创建新文档,存入用户信息,默认将IsActiveUser设为false
  • 登录时需要做两项检查:
    1. 验证Auth.auth().currentUser?.isEmailVerified为true(这部分正常工作)
    2. 从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是异步网络请求,代码执行顺序不是从上到下同步的:

  1. 调用getDocument后,立即继续执行后面的print和验证逻辑,此时isActive还是初始的nil
  2. 等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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 21:47:57