From 3dc6060a385d9bfcb4fa1f61262ba74776573229 Mon Sep 17 00:00:00 2001 From: Ryan Nystrom Date: Wed, 12 Jul 2017 12:40:10 -0700 Subject: [PATCH] Avoid rare crash when update queued during batch Summary: A new crash! I think this has been a pretty low-firing outlier for some time. Pretty excited to find this. Exposed when adding bg updating as things were likely to collide more frequently, instead of being blocked by main. Unit test fails w/out the patch applied to the updater. Depends on D5392269 Reviewed By: jeremycohen Differential Revision: D5392301 fbshipit-source-id: 5b24d1b41e2a0bdba2b6bc2bfa4f6eeeb36fc4f1 --- CHANGELOG.md | 4 ++++ Source/IGListAdapterUpdater.m | 4 ++++ Tests/IGListAdapterE2ETests.m | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd0d0050..829cd880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ The changelog for `IGListKit`. Also see the [releases](https://github.com/instag 3.1.0 (**upcoming release**) ----- +### Fixes + +- Prevent a crash when update queued immediately after item batch update. [Ryan Nystrom](https://github.com/rnystrom) (tbd) + 3.0.0 ----- diff --git a/Source/IGListAdapterUpdater.m b/Source/IGListAdapterUpdater.m index 5ed51e0a..75229b8b 100644 --- a/Source/IGListAdapterUpdater.m +++ b/Source/IGListAdapterUpdater.m @@ -94,6 +94,10 @@ } static NSArray *objectsWithDuplicateIdentifiersRemoved(NSArray> *objects) { + if (objects == nil) { + return nil; + } + NSMutableSet *identifiers = [NSMutableSet new]; NSMutableArray *uniqueObjects = [NSMutableArray new]; for (id object in objects) { diff --git a/Tests/IGListAdapterE2ETests.m b/Tests/IGListAdapterE2ETests.m index 70a192b3..9dcf810c 100644 --- a/Tests/IGListAdapterE2ETests.m +++ b/Tests/IGListAdapterE2ETests.m @@ -1480,4 +1480,45 @@ [self waitForExpectationsWithTimeout:30 handler:nil]; } +- (void)test_whenUpdateQueuedDuringBatch_thatUpdateCompletesWithoutCrashing { + [self setupWithObjects:@[ + genTestObject(@1, @4), + genTestObject(@2, @4), + genTestObject(@3, @4), + genTestObject(@4, @4), + ]]; + + IGTestObject *object = self.dataSource.objects[0]; + IGTestDelegateController *sectionController = [self.adapter sectionControllerForObject:object]; + + XCTestExpectation *expect1 = genExpectation; + XCTestExpectation *expect2 = genExpectation; + + [sectionController.collectionContext performBatchAnimated:YES updates:^(id batchContext) { + object.value = @3; + [batchContext deleteInSectionController:sectionController atIndexes:[NSIndexSet indexSetWithIndex:0]]; + + self.dataSource.objects = @[ + genTestObject(@2, @4), + genTestObject(@4, @4), + genTestObject(@1, @3), + ]; + [self.adapter performUpdatesAnimated:YES completion:^(BOOL finished) { + XCTAssertEqual([self.collectionView numberOfSections], 3); + XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 4); + XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 4); + XCTAssertEqual([self.collectionView numberOfItemsInSection:2], 3); + [expect1 fulfill]; + }]; + } completion:^(BOOL finished2) { + XCTAssertEqual([self.collectionView numberOfSections], 4); + XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 3); + XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 4); + XCTAssertEqual([self.collectionView numberOfItemsInSection:2], 4); + XCTAssertEqual([self.collectionView numberOfItemsInSection:3], 4); + [expect2 fulfill]; + }]; + [self waitForExpectationsWithTimeout:30 handler:nil]; +} + @end