Swift开发中Firebase注册时saveDataToFirestore因handler重复调用如何修复
问题根源
- 异步逻辑时序错误:
Auth.auth().createUser是异步执行的方法,你在调用后直接同步执行saveDataToFirestore,此时注册请求尚未返回,用户认证状态还未完成更新。 - 状态监听器使用不当:
addStateDidChangeListener是持久化的全局监听器,添加后只要不手动移除,任何Auth状态变化(包括注册流程中的内部状态切换、临时用户到正式用户的变更)都会触发回调,这就是日志中entry 2打印两次的原因。两次返回的用户ID不一致,是注册流程中Auth内部先生成临时用户、再替换为正式注册用户导致的。 - 监听器未释放:你存储了监听器的
handle但从未调用removeStateDidChangeListener移除,不仅会导致重复回调,还会引发内存泄漏。
修复方案
核心逻辑是将用户数据写入操作和注册结果绑定,不要用全局状态监听器处理单次注册后的写入需求,修改后的代码如下:
class UserViewModelTEMP : ObservableObject { @Published var user = [UserTEMP]() var handle: AuthStateDidChangeListenerHandle? private let db = Firestore.firestore() // 若需要全局监听用户登录状态,统一在init中添加 init() { handle = Auth.auth().addStateDidChangeListener { [weak self] (auth, user) in guard let self = self else { return } if let user = user { // 此处仅做全局状态更新,不执行业务写入操作 print("当前登录用户ID:\(user.uid)") } else { self.user = [] } } } // 实例销毁时移除监听器,避免内存泄漏和无效回调 deinit { if let handle = handle { Auth.auth().removeStateDidChangeListener(handle) } } func signUp (firstName: String, lastName: String, email: String, password: String, reenterPassword: String, handler: @escaping AuthDataResultCallback) { // 密码校验不通过时主动回调错误 if reenterPassword != password { let error = NSError(domain: "SignUpDomain", code: -1, userInfo: [NSLocalizedDescriptionKey: "两次输入密码不一致"]) handler(nil, error) return } // 发起注册请求 Auth.auth().createUser(withEmail: email, password: password) { [weak self] result, error in guard let self = self else { return } // 先将注册结果回调给View层处理UI状态 handler(result, error) // 注册成功后再执行写入Firestore逻辑 if let error = error { print("注册失败:\(error.localizedDescription)") return } guard let registeredUser = result?.user else { print("未获取到注册成功的用户信息") return } // 直接传入注册接口返回的确定用户ID,不需要再监听状态 self.saveDataToFirestore(firstName: firstName, lastName: lastName, email: email, uid: registeredUser.uid) } } func saveDataToFirestore(firstName: String, lastName: String, email: String, uid: String) { print("开始写入用户数据到Firestore") let ref = db.collection("users").document(uid) ref.setData([ "first_name" : firstName, "last_name" : lastName, "email" : email ]) { err in if let err = err { print("写入失败:\(err.localizedDescription)") } else { print("写入成功,文档ID:\(ref.documentID)") } } } }
修复说明
- 写入逻辑移到注册成功回调内部,确保只有注册完成拿到确定的用户ID后才执行写入,不会出现ID不一致问题
- 移除了写入方法内的状态监听器,从根源避免重复触发写入
- 补充了密码不匹配场景的错误回调,避免View层无响应
- 新增监听器的移除逻辑,解决内存泄漏问题
内容的提问来源于stack exchange,提问作者dbosk
相关产品推荐
相关产品推荐

