iOS15真机运行SwiftUI时List出现非预期行为问题求助
问题排查与解决方案
核心原因
该问题是iOS15对SwiftUI ForEach的ID唯一性校验逻辑比旧版本更严格,且对短时间内的连续数组更新敏感度更高触发的,常见诱因如下:
- 每次
onAppear调用startCombine时未清空旧订阅,导致多个订阅同时生效,offerRowViewModels被多次重复赋值,短时间内的连续更新被ForEach的Diffing算法误判存在重复ID OfferRowViewModel的id属性未绑定到Offer的唯一Firestore文档ID,重复初始化同一份Offer对应的OfferRowViewModel时生成了不同的ID,或者存在ID复用逻辑- Firestore先返回本地缓存数据、再返回云端最新数据,两次数据更新间隔极短,触发iOS15
ForEach的校验bug
修复步骤
1. 解决重复订阅问题
在startCombine方法开头清空已有订阅,避免多个订阅同时触发赋值,同时可加防抖逻辑过滤短时间的连续更新:
func startCombine(currentUserUid: String) { // 新增:清空旧订阅,避免重复触发 self.cancellables.removeAll() offerRepository .$offers .receive(on: RunLoop.main) // 可选新增:加防抖,过滤100ms内的连续更新,避免缓存+云端两次更新的影响 .debounce(for: .milliseconds(100), scheduler: RunLoop.main) .map { offers in offers .filter { offer in (currentUserUid != "" ? offer.userId == currentUserUid : false) } .map { offer in OfferRowViewModel(offer: offer, listingRepository: ListingRepository()) } } // 新增:赋值前校验ID唯一性,兜底避免重复 .map { viewModels in var uniqueIds = Set<String>() return viewModels.filter { viewModel in guard !uniqueIds.contains(viewModel.id) else { print("发现重复ID:\(viewModel.id),已过滤") return false } uniqueIds.insert(viewModel.id) return true } } .assign(to: \.offerRowViewModels, on: self) .store(in: &cancellables) }
2. 确认OfferRowViewModel的ID稳定性
检查OfferRowViewModel的id属性实现,确保直接绑定到对应Offer的Firestore唯一文档ID,不要使用每次初始化生成的UUID()作为id:
// 正确示例:OfferRowViewModel的id绑定到原始Offer的唯一ID class OfferRowViewModel: Identifiable { let id: String let offer: Offer init(offer: Offer, listingRepository: ListingRepository) { // 直接使用Offer的文档ID,不要自己生成UUID self.id = offer.id self.offer = offer // 其他初始化逻辑 } }
3. 优化onAppear调用逻辑
避免currentUserUid为空时触发无效请求,可增加判断:
.onAppear(perform: { guard !authSession.currentUserUid.isEmpty else { return } self.offerHistoryViewModel.startCombine(currentUserUid: self.authSession.currentUserUid) })
验证方法
在赋值给offerRowViewModels之前增加ID重复校验打印,确认是否存在中间态的重复ID:
.map { viewModels in let allIds = viewModels.map(\.id) let duplicateCount = allIds.count - Set(allIds).count if duplicateCount > 0 { print("检测到\(duplicateCount)个重复ID,ID列表:\(allIds)") } return viewModels }
内容的提问来源于stack exchange,提问作者jonthornham
相关产品推荐
相关产品推荐

