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

Firebase用户邮箱验证状态无法更新问题排查求助

问题描述

创建新用户时会发送邮箱验证邮件,但无法追踪状态变化,无法将Firestore中用户的isEmailVerified字段从false更新为true。调试控制台已确认用户文档存在,但该字段值始终保持false。


相关代码

User 模型

import FirebaseAuth

class User: Identifiable {
    let id: String
    let username: String
    let profileImageUrl: String
    let fullname: String
    let email: String
    var stats: UserStats
    var bio: String?
    var blocked : Int
    var reputation : Int
    var isEmailVerified: Bool

    var isFollowed = false
    var isCurrentUser: Bool { return Auth.auth().currentUser?.uid == self.id}
    
    init(dictionary: [String: Any]) {
        self.id = dictionary["uid"] as? String ?? ""
        self.username = dictionary["username"] as? String ?? ""
        self.profileImageUrl = dictionary["profileImageUrl"] as? String ?? ""
        self.fullname = dictionary["fullname"] as? String ?? ""
        self.email = dictionary["email"] as? String ?? ""
        self.stats = UserStats(followers: 0, following: 0)
        self.blocked = dictionary["blocked"] as? Int ?? 0
        self.reputation = dictionary["reputation"] as? Int ?? 0
        self.isEmailVerified = dictionary["isEmailVerified"] as? Bool ?? false
    }
}

AuthViewModel

class AuthViewModel: ObservableObject {
    @Published var userSession: Firebase.User?
    @Published var isAuthenticating = false
    @Published var error: Error?
    @Published var user: User?
    @Published var isLoading = false
    @Published var isEmailVerified: Bool = false

    static let shared = AuthViewModel()
    
    init() {
        userSession = Auth.auth().currentUser
        fetchUser()
    }
    
    func registerUser(email: String, password: String, username: String, fullname: String, profileImage : UIImage) {
        guard let imageData = profileImage.jpegData(compressionQuality: 0.3) else { return }
        let filename = NSUUID().uuidString
        let storageRef = Storage.storage().reference().child(filename)
        
        storageRef.putData(imageData, metadata: nil) { _ , error in
            if let error = error {
                ProgressHUD.showFailed("Failed to upload image: \(error.localizedDescription)", interaction: false)
                print("Failed to upload image \(error.localizedDescription)")
                return
            }
            
            storageRef.downloadURL { url, _ in
                guard let profileImageUrl = url?.absoluteString else { return }
                
                Auth.auth().createUser(withEmail: email, password: password) { result, error in
                    if let error = error {
                        ProgressHUD.showFailed("Error: \(error.localizedDescription)", interaction: false)
                        print("DEBUG: ERROR \(error.localizedDescription)")
                    }
                    
                    guard let user = result?.user else { return }
                    
                    // Send email verification
                    user.sendEmailVerification { error in
                        if let error = error {
                            print("DEBUG: Error sending email verification: \(error.localizedDescription)")
                        } else {
                            print("DEBUG: Email verification sent to \(user.email ?? "")")
                        }
                    }
                    
                    let data = ["email" : email,
                                "username": username.lowercased(),
                                "fullname": fullname,
                                "profileImageUrl": profileImageUrl,
                                "isEmailVerified": false,
                                "uid": user.uid]
                    
                    Firestore.firestore().collection("users").document(user.uid).setData(data) { _ in
                        self.userSession = user
                        self.fetchUser()
                    }
                }
            }
        }
    }
}

ContentView

struct ContentView: View {
    @State private var isShowing = false
    @EnvironmentObject var viewModel : AuthViewModel
    
    init() {
        addAuthStateListener()
    }
    
    private func addAuthStateListener() {
        Auth.auth().addStateDidChangeListener { auth, user in
            if let user = user {
                let userRef = Firestore.firestore().collection("users").document(user.uid)
                
                userRef.getDocument { (document, error) in
                    if let document = document, document.exists {
                        let data = document.data()
                        let isEmailVerified = data?["isEmailVerified"] as? Bool ?? false
                        print("Текущий статус \(isEmailVerified)")
                        
                        // Check if email is verified and update database
                        if user.isEmailVerified != isEmailVerified {
                            userRef.updateData(["isEmailVerified": user.isEmailVerified]) { error in
                                if let error = error {
                                    print("Error updating user document: \(error.localizedDescription)")
                                } else {
                                    print("User document updated successfully")
                                }
                            }
                        }
                    } else {
                        print("\(error?.localizedDescription)")
                    }
                }
            }
        }
    }
    
    var body: some View {
        Group {
            if viewModel.userSession != nil {
                if viewModel.isEmailVerified == true {......
            }
        }
    }
}

问题分析与修复方案

核心问题

  1. ViewModel状态未同步:AuthViewModel中的isEmailVerified字段始终是初始值false,没有在获取用户数据时更新。
  2. Firestore更新后未刷新本地数据:在Auth状态监听中更新Firestore文档后,没有同步更新ViewModel的状态,导致视图无法感知变化。

修复步骤

1. 完善AuthViewModel的fetchUser方法

确保获取用户数据时同步更新isEmailVerified:

func fetchUser() {
    guard let uid = userSession?.uid else { return }
    Firestore.firestore().collection("users").document(uid).getDocument { snapshot, _ in
        guard let dictionary = snapshot?.data() else { return }
        self.user = User(dictionary: dictionary)
        // 更新本地验证状态
        self.isEmailVerified = dictionary["isEmailVerified"] as? Bool ?? false
    }
}

2. 更新Auth状态监听逻辑

在ContentView的监听方法中,更新Firestore后调用fetchUser同步ViewModel数据:

private func addAuthStateListener() {
    Auth.auth().addStateDidChangeListener { auth, user in
        guard let user = user else { return }
        let userRef = Firestore.firestore().collection("users").document(user.uid)
        
        userRef.getDocument { (document, error) in
            guard let document = document, document.exists else {
                print(error?.localizedDescription ?? "文档不存在")
                return
            }
            
            let dbVerified = document.data()?["isEmailVerified"] as? Bool ?? false
            print("当前数据库状态 \(dbVerified)")
            
            if user.isEmailVerified != dbVerified {
                userRef.updateData(["isEmailVerified": user.isEmailVerified]) { error in
                    if let error = error {
                        print("更新文档失败: \(error.localizedDescription)")
                    } else {
                        print("文档更新成功")
                        // 同步更新ViewModel数据
                        self.viewModel.fetchUser()
                    }
                }
            } else {
                // 状态一致时确保ViewModel数据正确
                self.viewModel.isEmailVerified = user.isEmailVerified
            }
        }
    }
}

3. 补充用户验证后的状态同步

用户点击验证邮件链接后,Firebase Auth的isEmailVerified状态会更新,但本地需要重新获取用户信息。可以在App启动或用户进入App时自动调用fetchUser,也可以添加手动刷新按钮供用户触发。

额外建议

  • 发送验证邮件后,提示用户重启App或手动刷新以同步状态;
  • 检查Firestore权限规则,确保用户有权限更新自己的文档;
  • 可以在AuthViewModel中添加手动刷新方法,方便主动触发数据同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 08:27:03