Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to use both reloadItemsAtIndexPaths and reloadData if dataSource count may change?

I am investigating a crashing bug with a UICollectionView via Crashlytics that generally takes this form:

Fatal Exception: NSInternalInconsistencyException Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (25) must be equal to the number of items contained in that section before the update (27), plus or minus the number of items inserted or deleted from that section (1 inserted, 1 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).

I believe this is caused because I have a collectionView that periodically refreshes itself with data from a server, and that data from the server may have more or fewer items than are contained in the client UICollectionViewDataSource.

When I get new data from the server, I call reloadData on my collection view.

However, it is possible that, due to user interaction with my collection view before the network download had completed, I had called reloadItemsAtIndexPaths just before. reloadItemsAtIndexPaths does not appear to finish for at least a few hundred ms and many processor cycles. Hence, this crash, when the dataSource is updated in the middle of a reloadItemsAtIndexPaths.

Is there an 'immediate' form of reloadItemsAtIndexPaths? Or must I always be calling reloadData given my use case, which does appear to update everything immediately and leave the UICollectionView in a good state at the end.


Edit

Here is what I have done, per advice from TwoStraws:

    // Prevent data source from batch updating while we work
    self.dataSource.locked = YES;

    [self.collectionView performBatchUpdates:^{
        [self.collectionView reloadItemsAtIndexPaths:@[indexPath]];
    } completion:^(BOOL finished) {
        self.dataSource.locked = NO;
    }];

Then in my data source class, upon receipt of results from the server, I always call assignResults:

- (void)assignResults:(NSMutableArray *)newResults {
    if (!self.locked) {
        self.results = newResults;
        [self.delegate handleDataSourceUpdated:self];
    } else {
        self.pendingResults = newResults;
    }
}

- (void)setLocked:(BOOL)locked {
    _locked = locked;

    if (!locked && self.pendingResults) {
        [self assignResults:self.pendingResults];
        self.pendingResults = nil;
    }
}

As you can see, results only get assigned if the data source is not locked; otherwise they are assigned when the data source is unlocked by the UICollectionViewController. Note that all of these methods are happening on the main thread so I do not need to worry about synchronization of my boolean property, locked.

like image 413
esilver Avatar asked Dec 10 '15 18:12

esilver


People also ask

Why do I need to store data in multiple data sources?

Sometimes, this is for security reasons. An example of this is the storage of credit card information. You may wish to store the data elements in multiple data sources. If one of the data sources is compromised the data retrieved is useless without the data from other data sources.

What happens if the collection data shrinks after reloading?

If the collection data shrinks as a result of the reload, the collection view adjusts its scrolling offsets accordingly. You shouldn’t call this method in the middle of animation blocks where items are being inserted or deleted. Insertions and deletions automatically cause the collection’s data to be updated appropriately.

Do I need to connect to more than one data source?

Often, you will need to connect to more than one data source. Sometimes, this is for security reasons. An example of this is the storage of credit card information. You may wish to store the data elements in multiple data sources. If one of the data sources is compromised the data retrieved is useless without the data from other data sources.

How to connect to multiple datasources with Spring Boot?

However, if you need to connect to multiple datasources with Spring Boot, additional configuration is needed. You need to provide configuration data to Spring Boot, customized for each data source. The source code of our sample application is available on GitHub .


1 Answers

Race conditions are always complicated issues, as I'm sure you know. If I understand you correctly, you're modifying the data source of the collection view while it's still trying to reload itself, which would mean the solution is to keep a separate data store that gets copied across to the collection view's data source atomically.

So:

  • Collection view reads from data source A.
  • Network writes to data source B.
  • At a point you specify, copy B into A in one fell swoop.
  • Tell the collection view to reload.

That way the collection view should never have to worry about race conditions – it always reads from a fixed set of data as far as it's concerned.

like image 86
TwoStraws Avatar answered Sep 19 '22 13:09

TwoStraws