Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Diffable data source for TableView error on iOS 13: Inconsistent associations for moves

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?

like image 418
Fabiosoft Avatar asked Aug 01 '19 13:08

Fabiosoft


2 Answers

Updated Answer

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.


Original Answer (Possibly incorrect for this case, but may still useful if queues are the problem)

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

Advanced Data Sources WWDC 2019

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).

like image 113
Jasarien Avatar answered Nov 09 '22 04:11

Jasarien


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

  • Make sure that your Datasource Doesn't have any duplicates
  • if you can't avoid Duplicates, and you don't mind braking 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.

like image 39
Mostfa Essam Avatar answered Nov 09 '22 02:11

Mostfa Essam