Quote from Apple Engineer
If you have mutable or complex data objects, you should not use them as item identifiers directly but rather just use your objects' identifiers with diffable data source and then fetch the correct object from your data store when configuring the cell by referencing the identifier.
So in your gist for example, you should store the UUID identifier of your item in the data source instead of the whole object.
This will work, but it requires more work and bookkeeping and is arguably less efficient.
Adjust your identifier (model) such that
hash and
== changes whenever your displayed content changes
Code Block Swiftstruct MyItem: Hashable { |
let identifier = UUID() |
var value: String? |
|
func hash(into hasher: inout Hasher) { |
hasher.combine(identifier) |
hasher.combine(value) |
} |
|
static func == (lhs: MyItem, rhs: MyItem) -> Bool { |
lhs.identifier == rhs.identifier && value == value |
} |
} |
Many people recommended this approach, but it has the following
caveat: when
value changes, the model becomes a different identifier for the diffable data source and thus a simultaneous move of the item is decomposed into a delete + insert, which causes a visual bug, but nothing more.
Keep your model as is. First apply the snapshot as usual, then
apply the snapshot with
animatingDifferences: false in the completion handler.
Code Block SwiftdataSource.apply(snapshot, animatingDifferences: true, completion: { |
dataSource.apply(snapshot, animatingDifferences: false) |
}) |
The bug seems to be that on
apply with
animatingDifferences: true, diffable data source updates the configured cells
before internally updating the identifiers that stayed "equal" according to
==. Note: In the completion handler one could also call
collectionView.reloadData(), or anything else that forces the configured cells to be updated.