如何在UICollectionViewDiffableDataSource中忽略重复数据项
当前去重逻辑失效的核心原因是Fruit模型的Hashable/Equatable实现完全依赖实例初始化时自动生成的UUID:每次创建新的Fruit实例时,哪怕title完全一致,新实例的uuid是全新生成的值,和已有实例的uuid永远不相等,因此fruits.contains(fruit)判断永远返回false,重复数据会被持续插入列表。
针对你同时对接接口数据、本地操作数据,且页面会重复触发接口拉取的场景,统一按业务唯一标识判断对象同一性即可,不要使用临时生成的UUID做判等依据。
1. 修正模型的Hashable/Equatable实现
根据业务规则确定唯一判断维度:如果是接口返回的数据,优先使用后端下发的固定业务ID(比如fruitId)作为判等依据;如果是简化场景下名称唯一,就用title做判等。==方法和hash方法的判断逻辑必须保持一致:
enum Section { case main } struct Fruit: Hashable { // 接口场景替换为后端下发的业务ID:let fruitId: String var title: String static func == (lhs: Fruit, rhs: Fruit) -> Bool { // 业务上认为两个对象是同一项的判断规则,多字段判断就并列加&&条件 return lhs.title == rhs.title } func hash(into hasher: inout Hasher) { // 只组合参与==判断的字段 hasher.combine(title) } }
注意:如果本地新建的对象需要生成唯一ID,生成后必须做持久化存储,不要每次初始化模型时临时生成新的UUID,否则App重启、接口重新拉取时会生成全新ID,直接导致去重逻辑失效。
2. 封装通用的去重插入逻辑
写一个统一的数组插入方法,不管是本地新增还是接口返回的数据,都走这个方法自动过滤重复项:
// 存储水果的数组 private var fruits: [Fruit] = [] /// 统一的新增水果方法,自动过滤重复项 func addNewFruits(_ newItems: [Fruit]) { // 过滤掉已经存在的项 let uniqueItems = newItems.filter { !fruits.contains($0) } fruits.append(contentsOf: uniqueItems) updateDataSource() } func updateDataSource() { var snapshot = NSDiffableDataSourceSnapshot<Section, Fruit>() snapshot.appendSections([.main]) snapshot.appendItems(fruits) dataSource.apply(snapshot, animatingDifferences: true) }
原有调用逻辑替换为调用统一方法即可:
@objc func add2() { let fruit = Fruit(title: "Fruit: 1") addNewFruits([fruit]) } // 接口拉取完成的回调里,直接传入接口返回的数组 func loadRemoteFruits() { api.getFruitList { [weak self] result in guard let remoteList = try? result.get() else { return } self?.addNewFruits(remoteList) } } @objc func didTapAdd() { let actionSheet = UIAlertController(title: "Select Fruit", message: nil, preferredStyle: .actionSheet) actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) actionSheet.addAction(UIAlertAction(title: "Fruit 1", style: .default, handler: { [weak self] _ in let fruit = Fruit(title: "Fruit: 1") self?.addNewFruits([fruit]) })) present(actionSheet, animated: true) }
3. DiffableDataSource适配说明
UITableViewDiffableDataSource本身会根据模型的Hashable实现自动判断数据同一性,只要模型的判等逻辑符合业务规则,哪怕直接传入全量数据,系统也会自动计算差异,不会出现重复展示、UI刷新异常的问题。
如果接口返回的是全量最新数据(包含删除、内容修改的项),直接替换本地数组后全量应用snapshot即可,不需要手动计算差量。
- 如果业务上允许同名但属性不同的项(比如同名但规格、价格不同),把所有参与唯一判断的字段都加入
==和hash方法即可,核心原则是:业务上判定为同一项的两个对象,参与判等的字段必须完全一致;业务上不同的项,判等字段必须存在差异。 - 如果需要同时展示本地新增未上传、服务端下发的同名项,可以给模型增加来源标识字段,判等时同时判断唯一ID+来源,避免不同来源的同项被错误过滤。
内容的提问来源于stack exchange,提问作者stompy

