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的情况了。
额外的安全优化建议
- 避免强制unwrap(
!):即使修复了状态同步,也建议在代码中使用可选绑定来处理userInSession,比如修改ProfileView的loadUserData方法:
func loadUserData() { guard let currentUser = session.userInSession else { print("No user in session") return } profileViewModel.LoadAUser(userId: currentUser.uid) }
- 处理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 // ...其他属性同步赋值 } }
- 预览添加EnvironmentObject:你的
ProfileView_Previews没有注入SessionStore,运行预览会崩溃,修改一下:
struct ProfileView_Previews: PreviewProvider { static var previews: some View { ProfileView() .environmentObject(SessionStore()) } }
内容的提问来源于stack exchange,提问作者Gurmukh Singh
相关产品推荐
相关产品推荐

