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

SwiftUI+Firebase中View访问Session时出现Nil值的问题排查

问题根源:异步操作导致的状态不同步

你遇到的致命错误,核心原因是**isLoggedIn的更新和userInSession的赋值不同步**。

看你的SessionStore里的listenAuthenticationState方法:当Firebase Auth检测到用户登录后,你立刻把self.isLoggedIn = true,但获取Firestore用户数据的getDocument是异步操作——这意味着isLoggedIn变成true的时候,userInSession可能还没被赋值(还处于nil状态)。

而你的InitialView只判断session.isLoggedIn就跳转到MainView再到ProfileView,这时候session.userInSession其实还是nil,你强制unwrap(!)自然就触发崩溃了。


解决方案:让登录状态和用户数据同步更新

修改SessionStore的listenAuthenticationState方法,把isLoggedIn的赋值放到Firestore数据获取的回调里,确保只有当userInSession真正被赋值后,才标记用户为已登录:

func listenAuthenticationState() {
    handle = Auth.auth().addStateDidChangeListener({(auth, user) in
        if let user = user {
            let firestoreGetUser = Firestore.firestore().collection("users").document(user.uid)
            firestoreGetUser.getDocument{(document, error) in
                if let dict = document?.data() {
                    guard let decodedUser = try? User.init(fromDictionary: dict) else { 
                        // 如果解析失败,也要重置登录状态
                        self.isLoggedIn = false
                        self.userInSession = nil
                        return 
                    }
                    self.userInSession = decodedUser
                    self.isLoggedIn = true // 这里才更新登录状态,保证和userInSession同步
                    print("decoded user = \(decodedUser)")
                    print("user logged in")
                } else {
                    // 文档不存在或获取失败的情况
                    self.isLoggedIn = false
                    self.userInSession = nil
                }
            }
        } else {
            self.isLoggedIn = false
            self.userInSession = nil
            print("no one logged in")
        }
    })
}

这样修改后,isLoggedIn只会在userInSession成功赋值后才变为true,保证了两个状态的一致性,也就不会出现进入ProfileView时userInSession为nil的情况了。


额外的安全优化建议
  1. 避免强制unwrap(!):即使修复了状态同步,也建议在代码中使用可选绑定来处理userInSession,比如修改ProfileView的loadUserData方法:
func loadUserData() {
    guard let currentUser = session.userInSession else {
        print("No user in session")
        return
    }
    profileViewModel.LoadAUser(userId: currentUser.uid)
}
  1. 处理ProfileViewModel中的潜在崩溃:你的LoadAUser方法里用了dict!,如果文档数据为空也会崩溃,改成可选绑定:
func LoadAUser(userId: String) {
    Firestore.firestore().collection("users").document(userId).getDocument{(snapshot, error) in
        guard let snap = snapshot, let dict = snap.data() else { 
            print("error fetching data or empty document")
            return 
        }
        guard let decodedUser = try? User.init(fromDictionary: dict) else { 
            print("Failed to decode user")
            return 
        }
        print("decoded user - load user - \(decodedUser)")
        // 别忘了把解码后的数据赋值给ViewModel的属性,否则视图拿不到数据
        self.uid = decodedUser.uid
        self.email = decodedUser.email
        // ...其他属性同步赋值
    }
}
  1. 预览添加EnvironmentObject:你的ProfileView_Previews没有注入SessionStore,运行预览会崩溃,修改一下:
struct ProfileView_Previews: PreviewProvider {
    static var previews: some View {
        ProfileView()
            .environmentObject(SessionStore())
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 18:37:59