Xcode iOS应用Firestore数据无法在iPhone历史页显示求助
问题排查:Firestore数据加载后SwiftUI列表不显示内容
问题描述
我是iOS开发新手,使用Swift在Xcode中开发应用,预期功能为:扫描Firestore中known_UPC集合内的UPC条码后,该产品的UPC会显示在HistoryView的历史列表中。实际测试时,扫描条码后ProductDetailView可正常展示,但返回HistoryView后,历史列表无内容。日志显示已成功获取UPC及产品名称,且代码已进入显示列表的分支,却无法显示UPC。
HistoryView代码
// // HistoryView.swift // import SwiftUI import FirebaseAuth import Firebase import FirebaseFirestore struct HistoryView: View { @Binding var isBarcodeScannerOpen: Bool @State var isProductDetailOpen = false @State var scannedBarcodeDocumentID: String = "" @State var scannedBarcode: String = "" @State var userData: [BarcodeData] = [] // Data from Firestore @State private var showScanFirstProductScreen = true @State private var dataLoaded = false struct BarcodeData: Identifiable { let id = UUID() let UPC: String let productName: String } func fetchDataFromFirestore() { // Update so after we check if the user exists, we check if there are any scanned barcode documents that exist. If no, show a blank screen with "hello message" guard let user = Auth.auth().currentUser else { return } let db = Firestore.firestore() let usersCollection = db.collection("users") usersCollection.document(user.uid).getDocument { document, error in if let document = document, document.exists { if let barcodeScanned = document.data()?["barcode_scanned"] as? [[String: Any]] { var tempUserData: [BarcodeData] = [] dataLoaded = true for data in barcodeScanned { if let UPC = data["UPC"] as? String { // Fetch product name from known_UPC collection db.collection("known_UPC").document(UPC).getDocument { knownDocument, knownError in DispatchQueue.main.async { print("UPC in problem area is", UPC) if let knownDocument = knownDocument, knownDocument.exists { if let productName = knownDocument.data()?["product_name"] as? String { showScanFirstProductScreen = false tempUserData.append(BarcodeData(UPC: UPC, productName: productName)) print("productName in problem area is", productName) print("data loaded status in problem area is", dataLoaded) } } else { showScanFirstProductScreen = false print("This product is not in the known_UPC collection, UPC is", UPC) print("data loaded status in else statement of problem area is", dataLoaded) } } } } } userData = tempUserData } } else { dataLoaded = true print("User does not exist") print("data loaded status where user doesn't exist is", dataLoaded) } } } func barcodeScanned(barcode: String) { scannedBarcodeDocumentID = storeBarcode(barcode: barcode) isBarcodeScannerOpen = false scannedBarcode = barcode self.isProductDetailOpen = true } var body: some View { NavigationView { ZStack(alignment: .bottom){ Color(red: 241, green: 242, blue: 248).ignoresSafeArea() NavigationLink(destination: CameraControllerView(isPresented: $isBarcodeScannerOpen, barcode: barcodeScanned(barcode:)), isActive: $isBarcodeScannerOpen) { EmptyView() } NavigationLink(destination: ProductDetailView(scannedBarcodeDocumentID: scannedBarcodeDocumentID, barcode: scannedBarcode), isActive: $isProductDetailOpen) { EmptyView() } VStack { if showScanFirstProductScreen { Text("Scan your first product") .font(.title) .foregroundColor(.black) } else { if dataLoaded { List(userData) { data in Text(data.UPC) } .listStyle(PlainListStyle()) .background(Color.white) } else { //ProgressView("Loading...") Text("Data loaded status in else statement of vstack is false") .font(.title) .foregroundColor(.black) } } } Spacer().frame(height: 20) } .onAppear { fetchDataFromFirestore() } } } } struct HistoryView_Previews: PreviewProvider { static var previews: some View { HistoryView(isBarcodeScannerOpen: .constant(false)) } }
测试日志
UPC in problem area is 0851557003132 productName in problem area is Algae Renew Deep Conditioning Mask data loaded status in problem area is true
问题根源
- 异步请求时序错误:
db.collection("known_UPC").document(UPC).getDocument是异步操作,循环发起请求后立刻执行userData = tempUserData,此时异步请求尚未完成,tempUserData为空,导致列表无数据。 - 数据更新未触发视图刷新:异步请求完成后往
tempUserData添加数据时,userData已经被赋值为空数组,且未重新给userData赋值,SwiftUI无法感知数据变化。 - 返回HistoryView时未重新加载数据:默认情况下,从ProductDetailView返回时,HistoryView的
onAppear不会触发,导致无法获取最新的扫描记录。
修复方案
1. 修正Firestore数据加载逻辑(用DispatchGroup等待异步请求完成)
修改fetchDataFromFirestore函数,确保所有异步请求完成后再更新userData:
func fetchDataFromFirestore() { guard let user = Auth.auth().currentUser else { return } let db = Firestore.firestore() let usersCollection = db.collection("users") usersCollection.document(user.uid).getDocument { document, error in if let document = document, document.exists { if let barcodeScanned = document.data()?["barcode_scanned"] as? [[String: Any]] { var tempUserData: [BarcodeData] = [] let dispatchGroup = DispatchGroup() // 创建调度组 self.dataLoaded = false self.showScanFirstProductScreen = true for data in barcodeScanned { if let UPC = data["UPC"] as? String { dispatchGroup.enter() // 标记任务开始 db.collection("known_UPC").document(UPC).getDocument { knownDocument, knownError in defer { dispatchGroup.leave() } // 标记任务结束 DispatchQueue.main.async { print("UPC in problem area is", UPC) if let knownDocument = knownDocument, knownDocument.exists { if let productName = knownDocument.data()?["product_name"] as? String { tempUserData.append(BarcodeData(UPC: UPC, productName: productName)) print("productName in problem area is", productName) } } else { print("This product is not in the known_UPC collection, UPC is", UPC) } } } } } // 所有异步请求完成后统一更新数据 dispatchGroup.notify(queue: .main) { self.userData = tempUserData self.dataLoaded = true self.showScanFirstProductScreen = tempUserData.isEmpty // 无数据时显示引导文字 } } else { self.dataLoaded = true self.showScanFirstProductScreen = true } } else { self.dataLoaded = true self.showScanFirstProductScreen = true print("User does not exist") } } }
2. 返回HistoryView时自动刷新数据
在body中添加onChange监听,当从ProductDetailView返回时重新加载数据:
var body: some View { NavigationView { ZStack(alignment: .bottom){ // 原有视图内容... } .onAppear { fetchDataFromFirestore() } .onChange(of: isProductDetailOpen) { newValue in if !newValue { // 从ProductDetailView返回,重新加载历史记录 fetchDataFromFirestore() } } } }
内容的提问来源于stack exchange,提问作者auchegbu
相关产品推荐
相关产品推荐

