Swift数组添加元素问题:Firestore请求后外部函数中数组为空
问题分析与解决方案
问题根源
- Firestore的
getDocuments是异步网络请求,它的回调闭包会在数据请求完成后才执行。你在viewDidLoad里调用itemsList()后立刻执行print(items)和showCatalogVC(),这时候异步请求还没结束,items数组自然是空的。 showCatalogVC()方法里错误地重新实例化了一个CatalogVC,这个新实例的items数组初始为空,且没有触发数据请求,所以展示后也看不到内容。
修复方案
1. 确保数据加载完成后再执行后续逻辑
把需要依赖items数组的操作(比如打印、更新UI、跳转页面)放到getDocuments的回调闭包内,或者通过Combine监听@Published属性的变化:
方式一:回调内处理
修改itemsList方法,在数据加载完成后执行后续操作:
func itemsList(){ database.collection("catalog") .getDocuments { (snapshot, error) in self.items.removeAll() if let snapshot { for document in snapshot.documents{ let docData = document.data() let title: String = docData["title"] as? String ?? "" let imageUrl: String = docData["imageUrl"] as? String ?? "" let description: String = docData["description"] as? String ?? "" let price: Int = docData["price"] as? Int ?? 0 let item: Item = Item(title: title, price: price, description: description, imageUrl: imageUrl) self.items.append(item) } // 数据加载完成后执行后续操作 print(self.items) self.updateCatalogUI() // 更新textView内容 } if let error { print("获取数据失败:\(error.localizedDescription)") } } } // 新增更新UI的方法 private func updateCatalogUI() { let catalogText = items.map { "\($0.title) - ¥\($0.price)\n\($0.description)\n" }.joined() textViewCatalog.text = catalogText }
然后修改viewDidLoad,移除提前执行的操作:
override func viewDidLoad() { super.viewDidLoad() let settings = FirestoreSettings() Firestore.firestore().settings = settings itemsList() }
方式二:用Combine监听@Published变化
因为你使用了@Published属性,可以通过Combine监听数组变化,自动触发后续操作:
import Combine // 需要导入Combine框架 class CatalogVC: UIViewController { // ... 其他代码不变 private var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() let settings = FirestoreSettings() Firestore.firestore().settings = settings // 监听items的变化,主线程更新UI $items .receive(on: DispatchQueue.main) .sink { [weak self] updatedItems in print(updatedItems) self?.updateCatalogUI() } .store(in: &cancellables) itemsList() } // ... 其他代码不变 }
2. 修复showCatalogVC方法的错误
你当前在CatalogVC内弹出新的CatalogVC逻辑不合理,若要跳转其他页面,可修改为实例化目标VC并传递数据:
private func showProductListVC() { let storyboard = UIStoryboard(name: "Main", bundle: nil) let productVC = storyboard.instantiateViewController(withIdentifier: "ProductListVC") as! ProductListVC productVC.items = self.items // 传递加载好的数组 self.present(productVC, animated: true, completion: nil) }
然后在itemsList的回调内调用这个方法,确保数据加载完成后再跳转。
额外优化建议
- 用Firestore内置解码功能替换手动解析字段,减少出错概率:
struct Item: Codable { var title: String var price: Int var description: String var imageUrl: String } // 在itemsList中替换手动解析逻辑 if let snapshot { for document in snapshot.documents { do { let item = try document.data(as: Item.self) self.items.append(item) } catch { print("解码失败:\(error.localizedDescription)") } } }
- 完善错误处理,避免数据加载失败时无提示。
内容的提问来源于stack exchange,提问作者user20766085
相关产品推荐
相关产品推荐

