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

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)
    }
  }
}

排查关键点

  1. 数据被覆盖:每次调用grabUserJournals都会直接替换userJournals数组,循环多个journalID时,最后一次请求会覆盖之前所有结果。如果最后一个请求无数据,数组就会为空。
  2. 状态传递错误:SavedJournalsList用@State接收savedJournals,但@State是视图内部状态,传递的是值拷贝,无法响应userJournals的异步更新,导致sheet里的视图拿不到最新数据。
  3. Sheet初始化时机问题:sheet可能在Firestore数据加载完成前就已初始化,此时传递的userJournals还是空数组,后续数据更新不会同步到sheet内的视图。
  4. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 10:40:42