Firebase注册系统问题:用户名已占用仍可完成注册
解决用户名已占用仍能注册的问题
嘿,我一眼就看出问题所在了——Firebase的数据库查询是异步操作,你原来的代码逻辑顺序完全错了!
问题根源
你写的usernamesRef.observeSingleEvent是异步执行的,它的回调块会在Firebase返回数据之后才运行,但你在调用这个方法之后,立刻就执行了self.usernameVerified = true这些代码。这就导致:不管用户名是否被占用,usernameVerified都会被强制设为true,注册流程自然会继续走下去。
而且还有个小问题:你现在是拉取所有用户数据来比对用户名,这在用户量变大之后会非常低效,还浪费带宽。
解决方案
我们需要把用户名校验改成异步回调的方式,同时优化Firebase的查询逻辑,直接检查用户名是否存在,而不是拉取所有数据。
1. 重构用户名校验函数
先把isValidUsername改成带回调闭包的函数,这样我们能在校验完成后再处理后续逻辑:
func isValidUsername(username: String, completion: @escaping (Bool) -> Void) { let ref = Database.database().reference(fromURL: "https://heytest.firebaseio.com/") // 优化:直接查询是否存在该用户名的用户,无需拉取全部数据 let usersRef = ref.child("users") usersRef.queryOrdered(byChild: "username") .queryEqual(toValue: username) .observeSingleEvent(of: .value) { snapshot in if snapshot.exists() { // 用户名已被占用,更新UI并返回false self.usernameLabel.textColor = UIColor.red self.usernameLabel.text = "USERNAME TAKEN" self.usernameTextField.layer.addBorder(edge: UIRectEdge.bottom, color: UIColor.red, thickness: 1.5) completion(false) } else { // 用户名可用,恢复UI并返回true self.usernameLabel.textColor = UIColor.black self.usernameLabel.text = "USERNAME" self.usernameTextField.layer.addBorder(edge: UIRectEdge.bottom, color: UIColor.black, thickness: 1.5) completion(true) } } withCancel: { error in print(error.localizedDescription) completion(false) } }
2. 修改注册按钮的点击逻辑
因为用户名校验是异步的,我们需要等校验结果回来后,再判断是否执行注册流程。这里先做同步的邮箱和密码校验,再处理异步的用户名校验:
@IBAction func onSignUp(_ sender: Any) { print("Sign Up pressed") // 先校验输入不为空 guard let inputUsername = usernameTextField.text, !inputUsername.isEmpty, let inputEmail = emailTextField.text, !inputEmail.isEmpty, let inputPassword = passwordTextField.text, !inputPassword.isEmpty else { errorLabel.alpha = 1 shake(viewToShake: errorLabel) print("Please fill in all required fields!") return } // 同步校验邮箱和密码 let isEmailValid = isValidEmail(email: inputEmail) let isPasswordValid = isValidPassword(password: inputPassword) print("[SIGN UP] - Email: \(isEmailValid)") print("[SIGN UP] - Password: \(isPasswordValid)") // 如果邮箱或密码不合法,直接提示 if !isEmailValid || !isPasswordValid { errorLabel.alpha = 1 shake(viewToShake: errorLabel) print("Password/Email verification failed!") return } // 异步校验用户名 isValidUsername(username: inputUsername) { [weak self] isUsernameValid in guard let self = self else { return } print("[SIGN UP] - Username: \(isUsernameValid)") if isUsernameValid { // 所有校验通过,开始注册流程 Auth.auth().createUser(withEmail: inputEmail, password: inputPassword) { authResult, error in if let error = error { self.errorLabel.alpha = 1 self.errorLabel.text = error.localizedDescription self.shake(viewToShake: self.errorLabel) return } guard let user = authResult?.user else { return } // 保存用户信息到数据库 let ref = Database.database().reference(fromURL: "https://heytest.firebaseio.com/") let usersReference = ref.child("users").child(user.uid) let userValues = ["username": inputUsername, "email": inputEmail, "games-played": "0"] usersReference.updateChildValues(userValues) { err, _ in if let err = err { print(err.localizedDescription) return } print("[SIGN UP] - Successfully Signed Up") self.errorLabel.alpha = 0 // 跳转到TabBar页面 if let tabBarVC = self.storyboard?.instantiateViewController(withIdentifier: "TabBarViewController") { self.present(tabBarVC, animated: false) } } } } else { // 用户名已被占用,提示用户 self.errorLabel.alpha = 1 self.shake(viewToShake: self.errorLabel) print("Username verification failed! Username is taken.") } } }
额外注意点
- 使用
[weak self]是为了避免循环引用,防止内存泄漏; - 直接查询用户名的方式比拉取所有用户数据高效得多,还能避免暴露不必要的用户数据;
- 先做非空校验和同步校验,能提前拦截无效输入,减少不必要的异步请求。
内容的提问来源于stack exchange,提问作者BlueBear
相关产品推荐
相关产品推荐

