SwiftUI视图无法更新Firestore数据问题求助
问题:SwiftUI从Firestore获取数据无法在视图中展示
我正在开发SwiftUI应用,通过AdminPostViewModel从Firestore获取帖子数据,打印显示文档数量正确,但posts数组实际为空(遍历打印无输出),导致HomeView的底部sheet(或ScrollView)中无法展示帖子。静态数据能正常显示,排除视图渲染问题,问题出在Firestore数据解析环节。
相关代码
AdminPostViewModel.swift
import Foundation import FirebaseFirestore import Firebase import SwiftUI import Combine class AdminPostViewModel: ObservableObject { @Published var posts = [AdminPost]() private var db = Firestore.firestore() var cancellables = Set<AnyCancellable>() func fetchData() { db.collection("adminsPosts").addSnapshotListener { querySnapshot, error in if let error = error { print("Error fetching documents: \(error)") return } guard let documents = querySnapshot?.documents else { print("No documents in 'adminsPosts' collection") return } self.posts = documents.compactMap { queryDocumentSnapshot -> AdminPost? in try? queryDocumentSnapshot.data(as: AdminPost.self) } print("Fetched \(documents.count) posts.") // 打印正常 self.posts.forEach { post in print("Post titlle: \(post.title)") // 无输出 } } } }
AdminPost.swift
struct AdminPost: Codable, Identifiable { @DocumentID var id: String? var title: String var body: String var tags: String var imageUrl: String var timestamp: Date var isTrending: Bool var views: InT // 拼写错误 var numberOfComments: Int var numberOfReactions: Int var isFeatured: Bool }
HomeView.swift
struct HomeView: View { @StateObject private var viewModel = AdminPostViewModel() @State private var bottomSheetPosition: BottomSheetPosition = .relative(0.4) // 补全缺失的状态定义 var body: some View { // 补全基础视图(示例用Color作为根视图) Color.white .bottomSheet(bottomSheetPosition: self.$bottomSheetPosition, switchablePositions: [ .relative(0.4), .relativeTop(0.975) ], headerContent: { Text("The Latest") .padding(.leading,13) .font(.system(size: 22)) .fontWeight(.bold) }) { ForEach(viewModel.posts) { post in NewsContentView(post: post) .padding() } } .onAppear { viewModel.fetchData() } } }
NewsContentView.swift
struct NewsContentView: View { var post: AdminPost var body: some View { var body: some View { // 重复定义body VStack(alignment: .leading, spacing: 5) { HStack { VStack(alignment: .leading, spacing: 8) { if post.isTrending { Text("Trending") .font(.caption) .fontWeight(.bold) .foregroundColor(.red) } Text(post.title) .font(.headline) Text(post.body) .font(.subheadline) .lineLimit(3) .foregroundColor(.primary) } Spacer() // AsyncImage replacement for iOS 15 and newer if #available(iOS 15, *) { AsyncImage(url: URL(string: post.imageUrl)) { phase in if let image = phase.image { image.resizable() .aspectRatio(contentMode: .fill) } else if phase.error != nil { Color.gray // Indicates an error. } else { Color.gray // Placeholder while loading. } } .frame(width: 90, height: 90) .cornerRadius(10) } else { // Fallback for iOS versions before 15 Rectangle() .fill(Color.gray) .frame(width: 90, height: 90) .cornerRadius(10) } } HStack { Text("10 mins ago") // Placeholder for dynamic timestamp .font(.caption) .foregroundColor(.secondary) Spacer() Button(action: {}) { Image(systemName: "hand.thumbsup") Text(String(post.numberOfReactions)) } Button(action: {}) { Image(systemName: "message") Text(String(post.numberOfComments)) } Button("Share", action: {}) } .padding(.top, 5) Divider().padding(.horizontal, -16) } .padding(.bottom) } }
解决方案
1. 修复模型拼写错误
AdminPost中的views: InT是拼写错误,应为Int,这会直接导致Codable解析失败,compactMap后数组为空:
struct AdminPost: Codable, Identifiable { @DocumentID var id: String? var title: String var body: String var tags: String var imageUrl: String var timestamp: Date var isTrending: Bool var views: Int // 修正拼写 var numberOfComments: Int var numberOfReactions: Int var isFeatured: Bool }
2. 处理Firestore日期解析
Firestore的Timestamp类型需要显式配置解码器才能正确解析为Swift的Date,修改fetchData中的解析逻辑:
self.posts = documents.compactMap { doc -> AdminPost? in let decoder = Firestore.Decoder() decoder.dateDecodingStrategy = .timestamp // 适配Firestore Timestamp return try? decoder.decode(AdminPost.self, from: doc.data()) }
同时确保Firestore中timestamp字段存储的是Timestamp类型,而非字符串或其他格式。
3. 调试解析错误
将try?替换为do-catch,打印具体解析错误,定位字段问题:
self.posts = documents.compactMap { doc -> AdminPost? in do { let post = try doc.data(as: AdminPost.self) return post } catch { print("解析文档失败: \(error),文档ID: \(doc.documentID)") return nil } }
4. 主线程更新@Published变量
Firestore快照回调在后台线程,显式切换到主线程更新posts,确保SwiftUI能正确响应:
DispatchQueue.main.async { self.posts = documents.compactMap { ... } }
5. 修复NewsContentView重复body定义
删除NewsContentView中重复的var body: some View定义,保留一个即可。
内容的提问来源于stack exchange,提问作者Ancientplayer
相关产品推荐
相关产品推荐

