SwiftUI List返回详情页触发ID重复警告,Combine拉取Firestore数据排查求助
这个警告的核心原因有两个:
- 每次视图触发
onAppear时调用startCombine都会创建新的Combine订阅,旧订阅没有被销毁,多个订阅同时修改offerRowViewModels数组,容易出现中间状态的重复数据 OfferRowViewModel初始化时id默认是空字符串,id的赋值是通过Combine异步操作完成的,新数组生成的瞬间所有元素的id都是空串,ForEach检测到重复ID触发警告。你没有打印到重复ID是因为打印时机早于ForEach的检测时机、且异步赋值很快完成,你看到的都是赋值后的正常ID。
解决方案
1. 清理重复订阅
在OfferHistoryViewModel的startCombine方法开头,先清空之前的所有订阅,避免多个流同时修改数组:
func startCombine(currentUserUid: String) { // 新增:清空旧订阅,避免重复触发赋值 cancellables.removeAll() // 原有逻辑保持不变 for offerRowViewModel in self.offerRowViewModels { print("Before startCombine func: \(offerRowViewModel.id)") } // ... 剩余原有代码 }
2. 初始化时直接赋值OfferRowViewModel的id
Offer在初始化OfferRowViewModel时已经传入,不需要通过Combine异步获取id,直接在init里赋值即可,避免临时空ID的问题:
class OfferRowViewModel: ObservableObject, Identifiable { // 去掉初始空值,改为let类型保证id不可变 let id: String var listingRepository: ListingRepository // 其他属性保持不变 @Published var offer: Offer @Published var listing: Listing? private var cancellables = Set<AnyCancellable>() init(offer: Offer, listingRepository: ListingRepository) { // 新增:初始化时直接赋值id,避免异步赋值的空窗期 self.id = offer.id ?? UUID().uuidString self.offer = offer self.listingRepository = listingRepository self.startCombine() } func startCombine() { // 删掉原来通过$offer赋值id的Combine代码块,不需要了 // 只保留获取listing的逻辑即可 listingRepository .$listings .receive(on: RunLoop.main) .map { listings in listings.first(where: { $0.id == self.offer.listingId}) } .assign(to: \.listing, on: self) .store(in: &cancellables) } }
可选优化
如果不需要保留旧的列表数据,可以在startCombine开头先清空offerRowViewModels,避免旧数据残留:
func startCombine(currentUserUid: String) { cancellables.removeAll() // 新增:清空旧数组 offerRowViewModels.removeAll() // 原有逻辑保持不变 }
内容的提问来源于stack exchange,提问作者jonthornham
相关产品推荐
相关产品推荐

