DispatchGroup需两次调用才退出:Firebase注册后Firestore写入异常
问题分析与解决方案
看起来你遇到的核心问题是Firestore文档创建依赖的用户ID还未生成,就并行执行了Auth注册和Firestore写入操作,导致第一次点击时Firestore写入失败,第二次点击时因为之前的Auth注册已经完成、用户ID已存在,才成功执行写入。同时你的代码还存在类型转换错误,导致回调逻辑异常。
问题根源拆解
- 并行任务的依赖错误:你用
DispatchGroup同时启动了Auth注册和Firestore写入,但Firestore写入需要Auth生成的用户ID,这两个任务是串行依赖关系,不能并行执行。第一次点击时,user.ID还是初始值(大概率是空字符串),导致Firestore的document(user.ID)无效,写入操作失败或无效果。 - 类型转换错误:
signupUser方法返回的是AuthDataResult类型,你却尝试把它直接转成Firebase.User,导致第一次回调时firebaseUser为nil,触发错误分支,而你误以为是Firestore写入没执行。 - 重复点击未限制:注册按钮没做防重复点击处理,导致第二次点击时重复调用了Auth注册接口。
具体修复步骤
1. 修正Auth注册方法,返回正确的用户对象
在UsersDataModel.swift中,修改signupUser方法,确保返回的是Firebase.User类型:
func signupUser(user: User, password: String, _ completion: @escaping (_ result: Any?) -> Void) { Auth.auth().createUser(withEmail: user.email, password: password) { (authResult, err) in if let err = err { print("Error creating user \(err)") completion(err) } else { guard let firebaseUser = authResult?.user else { let error = NSError(domain: "AuthError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to retrieve user from auth result"]) completion(error) return } print("User signed up successfully") completion(firebaseUser) // 现在返回的是正确的Firebase.User对象 } } }
2. 改为串行执行任务,确保用户ID存在后再写入Firestore
在UsersHelper.swift中,去掉不必要的DispatchGroup,改为串行执行Auth注册和Firestore写入:
func signup(user: User, password: String, completion: @escaping (_ result: Any?) -> Void) { // 先完成Auth注册 usersDataModel.signupUser(user: user, password: password) { result in if let signupError = result as? Error { completion(signupError) return } // 拿到Auth生成的用户对象 guard let firebaseUser = result as? Firebase.User else { let error = NSError(domain: "AuthError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid user data returned"]) completion(error) return } // 将Auth生成的UID赋值给本地User模型 var updatedUser = user updatedUser.ID = firebaseUser.uid // 现在再执行Firestore写入 usersDataModel.create(user: updatedUser) { dbResult in if let dbError = dbResult as? Error { completion(dbError) return } // 两个操作都成功,返回Firebase用户对象 completion(firebaseUser) } } }
3. 给注册按钮添加防重复点击限制
在SignupViewController.swift的signupButtonTapped方法中,添加按钮状态控制:
@IBAction func signupButtonTapped(_ sender: UIButton) { // 点击后立即禁用按钮,防止重复触发 sender.isEnabled = false // 你的user和password获取逻辑... let user = User() // 替换成你实际的用户对象初始化逻辑 guard let password = passwordTextField.text else { sender.isEnabled = true return } usersHelper.signup(user: user, password: password) { result in defer { // 无论成功失败,都重新启用按钮 sender.isEnabled = true } guard let user = result as? Firebase.User else { let error = result as? Error self.handleSignupError(error!) return } self.performSegue(withIdentifier: "ShowGroupsFromSignupSegue", sender: self) } }
额外检查点
确保UsersDataModel.swift的create方法中,userData字典包含正确的字段,且使用user.ID作为文档ID:
func create(user: User, _ completion: @escaping (_ result: Any?) -> Void) { let userData: [String: Any] = [ "id": user.ID, "email": user.email, // 添加其他需要存储的用户字段 ] db.collection("users").document(user.ID).setData(userData) { err in if let err = err { print("There was an error creating the user \(err)") completion(err) } else { print("!User created in db successfully!") completion(nil) } } }
内容的提问来源于stack exchange,提问作者N0ug4t
相关产品推荐
相关产品推荐

