I have a problem with applying NSDiffableDataSourceSnapshot
to UICollectionViewDiffableDataSource
. Imagine this situation: I have two items and I want to remove the second one and also I want to reload all other items in that section.
I do it like this:
var snapshot = oldSnapshot
if let item = getterForItemToDelete() {
snapshot.deleteItems([item])
}
snapshot.reloadItems(otherItems)
But then in cell provider of data source app crashes since it tries to get cell for item identifier for which I no longer have data so I have to return nil
:
let dataSource = MyDataSource(collectionView: collectionView) { [weak self] collectionView, indexPath, identifier in
guard let viewModel = self?.viewModel.item(for: identifier) else { return nil }
...
}
What is strange is that when I try to debug it and print my items when I apply snapshot, it prints one item:
(lldb) print snapshot.itemIdentifiers(inSection: .mySection)
([App.MyItemIdentifier]) $R0 = 1 value {
[0] = item (item = "id")
}
But, immediately after this, in cell provider I get that I have two items, which I don't
(lldb) print self?.dataSource.snapshot().itemIdentifiers(inSection: .mySection)
([App.MyItemIdentifier]) $R1 = 2 values {
[0] = item (item = "ckufpa58100041ps6gmiu5zl6")
[1] = item (item = "ckufpa58100051ps69yrgaspv")
}
What is even more strange is that this doesn't happen when I have 3 items and I want to delete one and reload the others.
One workaround which solves my problem is to return empty cell instead of nil
in cell provider:
let dataSource = MyDataSource(collectionView: collectionView) { [weak self] collectionView, indexPath, identifier in
guard let viewModel = self?.viewModel.item(for: identifier) else {
return collectionView.dequeueReusableCell(withReuseIdentifier: "UICollectionViewCell", for: indexPath)
}
...
}
And the last strange thing, when I do this and then I look to View Hierarchy debugger, there is just one cell, so it seems that the empty cell gets removed.
Does anybody know what could I do wrong or is this just expected behavior? Since I didn't find any mention of providing cells for some sort of optimalizations, animations or something in the documentation.
You shouldn't be removing items from the snapshot. Remove them from your array. Create the dataSource again with the updated array, and call the snapshot with this newly created dataSource. CollectionView will automatically update with the new data. To put it more simply, change the array, then applySnapshot() again.