I'm updating my current UITableview to diffable datasource provided by iOS 13 UITableViewDiffableDataSource
.
I have an array with a custom object (implementing isEqual: method). On viewWillAppear I load the data from disk and call apply for snapshot.
-(void)updateTableViewAnimated:(BOOL)animated API_AVAILABLE(ios(13.0)){
NSDiffableDataSourceSnapshot *snapshot = [[NSDiffableDataSourceSnapshot alloc]init];
[snapshot appendSectionsWithIdentifiers:@[@"sectionTitle"]];
[snapshot appendItemsWithIdentifiers:self.playlists];
[self.diffDataSource applySnapshot:snapshot animatingDifferences:animated];
}
And everything loads. But when a try to delete an item from the array and call again updateTableViewAnimated:
, I get an exception.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Inconsistent associations for moves'
What does it mean? How can I solve?
I was fortunate enough to get a response to the bug I raised regarding this. It turns out that my model object was had incorrect Hashing and Equality checks.
My swift structs conformed to Hashable, but provided a custom implementation of Equatable, whereby I only compared the ID property to determine equality. This meant that it was possible for two objects to be considered equal, but have differing hashes, which confuses the diffing algorithm.
To solve it, I simply removed my custom implementation of Equatable, and used the synthesised version.
You state in your question that you implement isEqual
, the ObjC, analog to Swift's ==
, but you're probably not providing a hash
implementation that agrees with your isEqual
implementation in all cases.
I don't know if this is the same problem you're having, but in my case it was caused by the applySnapshot
method being called from different queues.
The Advanced Data Sources WWDC session mentioned that applySnapshot
must be exclusively called on a background queue OR the main queue, but don't call from both. Advanced Data Sources WWDC 2019 - 32:00
In my case I'm using a Combine publisher to react to changes on my data source, and that publisher was sometimes sending values on the main thread or a background thread. To solve my issue I added .receive(on: RunLoop.Main)
to the chain.
In your case, maybe you can wrap anything that makes a call to updateTableViewAnimated:
in a dispatch_async
call using the queue you want it to run on (be that main or background).
Adding to Jasarien informative answer,
you need to keep in mind that UICollectionViewDiffableDataSource
use hashable to differentiate between items in your datasource
if your model is a struct, and there're two items in your datasource model which happens to have the exact same values, Hashable
protocol will produce the same hashValue for them.
also Equatable
protocol will return true! , which will confuse
UICollectionViewDiffableDataSource
Solutions
Equatable
, you might want to add random Value in your datasource, but it's not recommended since the values generated might be equal at any coincidence, as stated in Documentions
*low probability, but it might happen * Depending on the size and span of range, some concrete values may be represented more frequently than others.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With