点击其他视图按钮后,调用Firestore的onAppear视图为空问题
问题描述
我在另一个视图点击按钮后加载当前RewardsView,使用模拟数据时视图显示正常,但切换为调用Firestore数据库数据后,视图无内容显示。我学习SwiftUI仅数月,项目其他部分用相同方式调用数据库是正常的,附上代码示例,怀疑是对SwiftUI视图运行时构建逻辑理解有误,寻求帮助。
代码示例
import SwiftUI import FirebaseFirestore import FirebaseFirestoreSwift struct RewardsView: View { // Dummy Data // var rewardItems: [Reward] = [ // .init(RewardText: "10% Off Next Haircut", RewardRedeemed: false, rewardPhoto: "crown_kings_main_photo", costOfReward: 10), // .init(RewardText: "10% Off Extra Service", RewardRedeemed: true, rewardPhoto: "crown_kings_main_photo", costOfReward: 8), // .init(RewardText: "20% Next Haircut", RewardRedeemed: false, rewardPhoto: "crown_kings_main_photo", costOfReward: 15), // .init(RewardText: "30% off Next Haircut", RewardRedeemed: false, rewardPhoto: "crown_kings_main_photo", costOfReward: 20), // .init(RewardText: "25% Off Next Haircut", RewardRedeemed: true, rewardPhoto: "crown_kings_main_photo", costOfReward: 18), // .init(RewardText: "45% Off Next Haircut", RewardRedeemed: true, rewardPhoto: "crown_kings_main_photo", costOfReward: 30) // ] @EnvironmentObject private var rewardsViewModel: RewardViewModel @EnvironmentObject private var loyaltyViewModel: LoyaltyViewModel @State var rewards: [Reward] = [] func getRewards() async throws -> [Reward]{ let db = Firestore.firestore() let snapshot = try await db.collection("redeemRewards").getDocuments() let rewards = snapshot.documents.compactMap{try? $0.data(as: Reward.self)} return rewards } var body: some View { NavigationStack{ ScrollView { LazyVGrid (columns: [ GridItem(.flexible()), GridItem(.flexible()), ], spacing: 16, content:{ ForEach(rewardsViewModel.rewards, id: \.self) { reward in RewardsCardView(rewardPhoto:reward.rewardPhoto, rewardText: reward.rewardText, rewardRedeemed: reward.rewardRedeemed, costOfReward: reward.costOfReward) .environmentObject(rewardsViewModel) .environmentObject(loyaltyViewModel) } }) } .onAppear { Task{ do { let newRewards = try await getRewards() rewards.append(contentsOf: newRewards) } catch { print("Error Fetching rewards: \(error.localizedDescription)") } } } .navigationTitle("Rewards") } } } //#Preview { // RewardsView() //} struct Reward: Identifiable, Codable,Hashable { @DocumentID var id: String? let rewardText: String let rewardRedeemed: Bool let rewardPhoto: String let costOfReward: CGFloat let timestamp: Timestamp let userId: String let remainingPoints: CGFloat func hash(into hasher: inout Hasher) { hasher.combine(id) } static func == (lhs: Reward, rhs: Reward) -> Bool { lhs.id == rhs.id } }
问题分析与修复方案
核心问题1:数据源不匹配
你在LazyVGrid中遍历的是rewardsViewModel.rewards,但onAppear里把Firestore获取到的数据存在了本地@State var rewards数组中,完全没有更新ViewModel的数据源,这是视图无内容的直接原因。
核心问题2:解析错误无反馈
compactMap{try? $0.data(as: Reward.self)}使用try?会把解析错误吞掉,导致即使Firestore返回了数据,只要解析失败就会返回空数组,你却看不到任何错误提示。
其他潜在问题
onAppear在NavigationStack的缓存机制下可能不会触发(比如从其他视图返回时),不如.task修饰符可靠。- 需确认Firestore文档字段名和
Reward结构体字段完全一致(比如结构体用rewardText,Firestore里不能写成RewardText),否则解析会失败。 - 需确认
RewardViewModel中的rewards是@Published属性,否则更新数组不会触发视图刷新。
修复步骤
修正数据源更新逻辑
将获取到的数据更新到ViewModel的rewards数组:.onAppear { Task{ do { let newRewards = try await getRewards() // 替换为更新ViewModel的数组 rewardsViewModel.rewards = newRewards } catch { print("获取奖励失败: \(error.localizedDescription)") } } }强化错误排查
把try?改成try,让解析错误能被捕获并打印:func getRewards() async throws -> [Reward]{ let db = Firestore.firestore() let snapshot = try await db.collection("redeemRewards").getDocuments() // 用try替代try?,解析失败会抛出明确错误 let rewards = try snapshot.documents.map{try $0.data(as: Reward.self)} return rewards }替换onAppear为task
使用.task替代.onAppear,确保异步任务在视图出现时可靠执行:.task { do { let newRewards = try await getRewards() rewardsViewModel.rewards = newRewards } catch { print("获取奖励失败: \(error.localizedDescription)") } }检查ViewModel属性
确保RewardViewModel中的rewards是@Published:class RewardViewModel: ObservableObject { @Published var rewards: [Reward] = [] // 其他逻辑 }
内容的提问来源于stack exchange,提问作者Joel Carter
相关产品推荐
相关产品推荐

