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

如何在Firebase用户注册时实现唯一用户名校验?

Firebase 用户名唯一性校验实现方案

你的原有代码里if username == username是无效判断,根本没有查询Firebase数据库,下面是完整的修正方案:

1. 在ViewModel中添加用户名查询方法

首先在你的ViewModel里实现异步查询Firestore集合的逻辑,判断用户名是否已存在:

import FirebaseFirestore
import FirebaseAuth

class AuthViewModel: ObservableObject {
    private let firestore = Firestore.firestore()
    
    // 检查用户名是否存在
    func checkUsernameExists(_ username: String, completion: @escaping (Bool) -> Void) {
        // 统一转为小写,避免大小写导致的重复问题
        let lowercasedUsername = username.lowercased()
        firestore.collection("users") // 假设用户数据存储在名为users的集合中
            .whereField("username", isEqualTo: lowercasedUsername)
            .getDocuments { snapshot, error in
                if let error = error {
                    print("查询用户名失败:\(error.localizedDescription)")
                    completion(false)
                    return
                }
                // 如果查询到文档,说明用户名已存在
                let usernameExists = snapshot?.documents.isEmpty == false
                completion(usernameExists)
            }
    }
    
    // 原有注册方法补充错误处理
    func registerUser(email: String, password: String, username: String, fullname: String, profileImage: UIImage) {
        Auth.auth().createUser(withEmail: email, password: password) { [weak self] authResult, error in
            guard let self = self else { return }
            if let error = error {
                ProgressHUD.showFailed(error.localizedDescription)
                return
            }
            
            // 这里省略头像上传到Storage的逻辑,自行补充
            guard let uid = authResult?.user.uid, let profileImageUrl = "" else { return }
            
            let userData = [
                "uid": uid,
                "email": email,
                "username": username.lowercased(), // 存储统一小写的用户名
                "fullname": fullname,
                "profileImageUrl": profileImageUrl
            ] as [String: Any]
            
            self.firestore.collection("users").document(uid).setData(userData) { error in
                if let error = error {
                    // 捕获Firestore唯一索引冲突错误(错误码5)
                    if (error as NSError).code == 5 {
                        ProgressHUD.showFailed("该用户名已存在")
                    } else {
                        ProgressHUD.showFailed(error.localizedDescription)
                    }
                    return
                }
                ProgressHUD.showSuccess("注册成功")
            }
        }
    }
}

2. 修改SwiftUI视图中的按钮逻辑

在你的注册视图中添加加载状态控制,修改按钮的点击逻辑:

struct SignUpView: View {
    @State private var email = ""
    @State private var password = ""
    @State private var username = ""
    @State private var fullname = ""
    @State private var selectedUIImage: UIImage?
    @State private var checked = false
    @State private var isCheckingUsername = false // 控制加载状态
    @StateObject private var viewModel = AuthViewModel()
    
    var body: some View {
        Button(action: {
            guard !username.isEmpty else { return }
            isCheckingUsername = true
            // 调用ViewModel的查询方法
            viewModel.checkUsernameExists(username) { exists in
                isCheckingUsername = false
                if exists {
                    ProgressHUD.showFailed("该用户名已存在")
                } else {
                    guard let image = selectedUIImage else { return }
                    viewModel.registerUser(email: email,
                                       password: password,
                                       username: username,
                                       fullname: fullname,
                                       profileImage: image)
                }
            }
        }, label: {
            if checked && !email.isEmpty && !username.isEmpty && !fullname.isEmpty && !password.isEmpty && selectedUIImage != nil && !isCheckingUsername {
                Text("注册")
                    .font(.headline)
                    .foregroundColor(.blue)
                    .frame(width: 360, height: 50)
                    .background(Color.white)
                    .clipShape(Capsule())
                    .padding()
            } else {
                Text(isCheckingUsername ? "检查中..." : "注册")
                    .font(.headline)
                    .foregroundColor(.gray)
                    .frame(width: 360, height: 50)
                    .background(Color.white.opacity(0.6))
                    .clipShape(Capsule())
                    .padding()
            }
        })
    }
}

3. 关键补充:添加Firestore唯一索引

为了避免并发注册导致的竞态问题(两个用户同时注册同一个用户名),必须给users集合的username字段添加唯一索引:

  • 当第一次出现索引冲突错误时,Firebase控制台会提供一个自动创建索引的链接,点击即可完成配置;
  • 也可以手动进入Firestore控制台,在「索引」页面创建新索引,选择users集合,字段选username,勾选「唯一」选项。

内容的提问来源于stack exchange,提问作者s1mpledreamer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 15:25:32