登录/注册后MainTabView无法获取currentUser导致加载异常求助
嘿,我看了你的代码和问题描述,这个间歇性的loading和崩溃问题大概率是异步逻辑的时序和主线程更新的问题导致的,我帮你拆解下问题点和修复方案:
问题根源分析
- 注册后未主动拉取用户数据:你的
register方法里创建用户并写入Firestore后,没有调用fetchUser(),只有登录和App初始化时才会拉取用户数据。这就导致注册完成后currentUser还是nil,直接进入MainTabView的else分支显示loading,只有下次启动App时才会加载用户信息。 - 异步回调未在主线程更新UI:Firebase的Auth和Firestore回调都是在后台线程执行的,而SwiftUI的
@Published变量必须在主线程更新,否则会导致UI更新异常,甚至出现崩溃的情况。 - fetchUser的错误处理缺失:当Firestore文档不存在或者解析失败时,没有任何处理逻辑,会导致
currentUser一直处于nil状态,持续显示loading。 - 头像上传后的用户数据不同步:注册时上传头像并更新Firestore后,没有重新拉取用户数据,导致AccountView里的头像URL可能不是最新的。
具体修复方案
1. 修复register方法,补全fetchUser并确保主线程更新
修改AuthViewModel里的register方法,在写入Firestore成功后调用fetchUser,同时把更新userSession的操作放到主线程:
func register(withEmail email: String, password: String, fullname: String) { Auth.auth().createUser(withEmail: email, password: password) { [weak self] result, error in guard let self = self else { return } if let error = error { print("DEBUG: Failed to register with error \(error.localizedDescription)") return } guard let user = result?.user else { return } // 主线程更新userSession,确保UI及时响应 DispatchQueue.main.async { self.userSession = user } let data: [String: Any] = ["email": email, "fullname": fullname] COLLECTION_USERS .document(user.uid) .setData(data) { [weak self] error in guard let self = self else { return } if let error = error { print("DEBUG: Failed to save user data \(error.localizedDescription)") return } // 数据写入成功后拉取用户信息 self.fetchUser() self.uploadProfileImage(self.selectedImage) } } }
2. 确保所有@Published变量的更新在主线程
修改login、fetchUser、signOut方法,把更新userSession和currentUser的操作都放到主线程,同时补全错误处理:
func login(withEmail email: String, password: String) { Auth.auth().signIn(withEmail: email, password: password) { [weak self] result, error in guard let self = self else { return } if let error = error { print("DEBUG: Failed to sign in with error \(error.localizedDescription)") return } guard let user = result?.user else { return } DispatchQueue.main.async { self.userSession = user } self.fetchUser() } } func fetchUser() { guard let uid = userSession?.uid else { return } COLLECTION_USERS.document(uid).getDocument { [weak self] snapshot, error in guard let self = self else { return } if let error = error { print("DEBUG: Failed to fetch user \(error.localizedDescription)") // 拉取失败时清除userSession,回到登录页 DispatchQueue.main.async { self.userSession = nil } return } guard let snapshot = snapshot, snapshot.exists else { print("DEBUG: User document does not exist") DispatchQueue.main.async { self.userSession = nil } return } do { let user = try snapshot.data(as: User.self) DispatchQueue.main.async { self.currentUser = user } } catch { print("DEBUG: Failed to parse user data \(error.localizedDescription)") } } } func signOut() { DispatchQueue.main.async { self.userSession = nil } do { try Auth.auth().signOut() } catch { print("DEBUG: Failed to sign out \(error.localizedDescription)") } }
3. 修复头像上传后的用户数据同步
在头像上传完成并更新Firestore后,重新拉取用户数据,确保UI显示最新的头像:
func uploadProfileImage(_ image: UIImage?) { guard let uid = userSession?.uid else { return } ImageUploader.uploadImage(image: image) { [weak self] profileImageUrl in guard let self = self else { return } COLLECTION_USERS .document(uid) .updateData(["profileImageUrl": profileImageUrl]) { [weak self] error in guard let self = self else { return } if let error = error { print("DEBUG: Failed to update profile image \(error.localizedDescription)") return } // 更新头像后重新拉取用户数据 self.fetchUser() } } }
4. 优化MainTabView的loading状态(可选)
把纯文本的loading换成系统自带的加载指示器,体验更友好:
struct MainTabView: View { @State private var selectedIndex = 0 @EnvironmentObject var viewModel: AuthViewModel var body: some View { Group { if let user = viewModel.currentUser { TabView(selection: $selectedIndex) { ExploreView() .onTapGesture { self.selectedIndex = 0 } .tabItem { Image(systemName: "house") }.tag(0) SearchView() .onTapGesture { self.selectedIndex = 1 } .tabItem { Image(systemName: "magnifyingglass") }.tag(1) ConversationsView() .onTapGesture { self.selectedIndex = 2 } .tabItem { Image(systemName: "message") }.tag(2) AccountView(user: user) .onTapGesture { self.selectedIndex = 3 } .tabItem { Image(systemName: "person.crop.circle") }.tag(3) } } else { ProgressView("Loading...") .progressViewStyle(CircularProgressViewStyle()) } } } }
额外小建议
- 用
[weak self]避免循环引用,防止内存泄漏 - 所有Firebase回调都要处理错误,方便调试和给用户提示
- 拉取用户数据失败时自动清除
userSession,避免用户一直卡在loading状态
内容的提问来源于stack exchange,提问作者amelia
相关产品推荐
相关产品推荐

