Firestore数据无法同步到@Published变量,跨视图调用为空
问题描述
我通过Firestore拉取数据并存储到@Published var userJournals中,初始拉取时已确认数据能成功映射,但在另一个视图调用该变量时却为空,求排查问题原因。
视图1:JournalDashLogic 逻辑类
class JournalDashLogic: ObservableObject { @Published var userJournals = [UserJournalEntry]() @Published var userJournalIDs = [String]() func grabUserJournals(journalID: String) { // 获取当前用户 guard let uid = FirebaseManager.shared.auth.currentUser?.uid else { return } FirebaseManager.shared.firestore .collection("users") .document(uid) .collection("userJournalEntrys") .document(journalID) .collection("items") .addSnapshotListener { (snapshot, err) in guard let documents = snapshot?.documents else { print("no documents present") return } // 映射为日志条目结构体 self.userJournals = documents.map { (querySnapshot) -> UserJournalEntry in let data = querySnapshot.data() let dateCreated = data["dateCreated"] as? String ?? "" let dayOfWeek = data["dayOfWeek"] as? String ?? "" let mealCalories = data["mealCalories"] as? Int ?? 0 let mealCarbs = data["mealCarbs"] as? Int ?? 0 let mealFat = data["mealFat"] as? Int ?? 0 let mealName = data["mealName"] as? String ?? "" let mealProtein = data["mealProtein"] as? Int ?? 0 let MealServing = data["MealServing"] as? Int ?? 0 let mealSaved = data["saved"] as? Bool ?? false let mealTiming = data["timeCreated"] as? String ?? "" let entry = UserJournalEntry( id: UUID().uuidString, mealName: mealName, mealFat: mealFat, mealCarbs: mealCarbs, mealProtein: mealProtein, mealCalories: mealCalories, MealServing: MealServing, mealSaved: mealSaved, mealTiming: mealTiming, dayOfWeek: dayOfWeek, totalCalories: "100", dateCreated: dateCreated) return entry } } } }
视图2:触发数据拉取的代码
.onAppear { for id in jm.userJournalIDs { jm.grabUserJournals(journalID: id) } }
视图2中弹出的Sheet
.sheet(isPresented: $showAllJournals) { SavedJournalsList(savedJournalIDs: jm.userJournalIDs, savedJournals: jm.userJournals) .transition(transition) }
视图3:SavedJournalsList 展示页
struct SavedJournalsList: View { @State var savedJournalIDs: [String] @State var savedJournals: [UserJournalEntry] var body: some View { VStack(alignment: .leading) { ForEach(savedJournals, id: \.self) { entry in HStack { Text(entry.dateCreated).bold() Text("Total Calories: 3200") .padding(.leading, 15) } .frame(maxWidth: .infinity) .multilineTextAlignment(.center) HStack { Text("200 Carbs") .foregroundColor(.gray) Text("250 Protein") .foregroundColor(.gray) Text("100 Fat") .foregroundColor(.gray) } .padding(.all, 5) .frame(maxWidth: .infinity) } .frame(width: 300, height: 100) .background(.white) .cornerRadius(15) .shadow(color: Color("LighterGray"), radius: 5, x: 0, y: 8) } } }
排查关键点
- 数据被覆盖:每次调用
grabUserJournals都会直接替换userJournals数组,循环多个journalID时,最后一次请求会覆盖之前所有结果。如果最后一个请求无数据,数组就会为空。 - 状态传递错误:
SavedJournalsList用@State接收savedJournals,但@State是视图内部状态,传递的是值拷贝,无法响应userJournals的异步更新,导致sheet里的视图拿不到最新数据。 - Sheet初始化时机问题:sheet可能在Firestore数据加载完成前就已初始化,此时传递的
userJournals还是空数组,后续数据更新不会同步到sheet内的视图。 - ForEach渲染异常:
ForEach(savedJournals, id: \.self)要求UserJournalEntry遵循Hashable协议,如果没实现,会导致渲染失败,看起来像数组为空。
修复建议
- 修改数据存储逻辑:把
self.userJournals = documents.map { ... }改成self.userJournals.append(contentsOf: documents.map { ... }),避免覆盖之前的数据;或者按journalID分组存储,防止数据丢失。 - 正确传递状态:让
SavedJournalsList直接观察JournalDashLogic实例,用@ObservedObject替代值传递:
struct SavedJournalsList: View { @ObservedObject var journalLogic: JournalDashLogic var body: some View { VStack(alignment: .leading) { ForEach(journalLogic.userJournals, id: \.id) { entry in // 原有视图代码 } } } }
然后在sheet中传递实例:
.sheet(isPresented: $showAllJournals) { SavedJournalsList(journalLogic: jm) .transition(transition) }
- 让模型遵循Identifiable:给
UserJournalEntry添加Identifiable协议,避免ForEach用\.self带来的问题:
struct UserJournalEntry: Identifiable { let id: String // 其他属性 }
- 添加加载状态:在
JournalDashLogic中加@Published var isLoading = false,请求开始时设为true,完成后设为false,视图中根据状态显示占位符,避免过早展示空视图。
内容的提问来源于stack exchange,提问作者Swink
相关产品推荐
相关产品推荐

