Handle nil section controllers from data source

Summary:
Removing the assert and handling `nil` section controllers. This wont effect Swift code which demands a non-optional section controller, but Objective-C is more nuanced. There is evidence that some callers do not have a default state and are returning `nil`.

Unfortunately this will [OOB here](https://github.com/Instagram/IGListKit/blob/master/Source/Internal/IGListSectionMap.m#L63) if using the original `objects` array provided by the data source. Instead we prune the array, only allowing in objects that have a matching section controller.

Fixes t15773862 internally.

- [x] All tests pass. Demo project builds and runs.
- [x] I added tests, an experiment, or detailed why my change isn't tested.
- [x] I added an entry to the `CHANGELOG.md` for any breaking changes, enhancements, or bug fixes.
Closes https://github.com/Instagram/IGListKit/pull/488

Reviewed By: dshahidehpour

Differential Revision: D4553886

Pulled By: rnystrom

fbshipit-source-id: a473a99b5eb513e4b610019dba0394a014193fc4
This commit is contained in:
Ryan Nystrom 2017-02-14 08:01:16 -08:00 committed by Facebook Github Bot
parent a5a08d2e11
commit 9c37fbf8a4
4 changed files with 23 additions and 5 deletions

View file

@ -30,6 +30,10 @@ This release closes the [3.0.0 milestone](https://github.com/Instagram/IGListKit
- You can now manually move items (cells) within a section controller, ex: `[self.collectionContext moveInSectionController:self fromIndex:0 toIndex:1]`. [Ryan Nystrom](https://github.com/rnystrom) [(#418)](https://github.com/Instagram/IGListKit/pull/418)
### Fixes
- Gracefully handle a `nil` section controller returned by an `IGListAdapterDataSource`. [Ryan Nystrom](https://github.com/rnystrom) [(tbd)](https://github.com/Instagram/IGListKit/pull/tbd)
2.2.0
-----

View file

@ -476,7 +476,9 @@
}
#endif
NSMutableArray<IGListSectionController <IGListSectionType> *> *sectionControllers = [[NSMutableArray alloc] init];
NSMutableArray<IGListSectionController <IGListSectionType> *> *sectionControllers = [NSMutableArray new];
NSMutableArray *validObjects = [NSMutableArray new];
IGListSectionMap *map = self.sectionMap;
// collect items that have changed since the last update
@ -498,8 +500,9 @@
sectionController = [dataSource listAdapter:self sectionControllerForObject:object];
}
IGAssert(sectionController != nil, @"Data source <%@> cannot return a nil section controller.", dataSource);
if (sectionController == nil) {
IGLKLog(@"WARNING: Ignoring nil section controller returned by data source %@ for object %@.",
dataSource, object);
continue;
}
@ -516,12 +519,13 @@
}
[sectionControllers addObject:sectionController];
[validObjects addObject:object];
}
// clear the view controller and collection context
IGListSectionControllerPopThread();
[map updateWithObjects:objects sectionControllers:sectionControllers];
[map updateWithObjects:validObjects sectionControllers:sectionControllers];
// now that the maps have been created and contexts are assigned, we consider the section controller "fully loaded"
for (id object in updatedObjects) {

View file

@ -1060,4 +1060,12 @@ XCTAssertEqual(CGPointEqualToPoint(point, p), YES); \
[mockDelegate verify];
}
- (void)test_whenDataSourceDoesntHandleObject_thatObjectIsDropped {
// IGListTestAdapterDataSource does not handle NSStrings
self.dataSource.objects = @[@1, @"dog", @2];
[self.adapter reloadDataWithCompletion:nil];
NSArray *expected = @[@1, @2];
XCTAssertEqualObjects(self.adapter.objects, expected);
}
@end

View file

@ -20,8 +20,10 @@
}
- (IGListSectionController <IGListSectionType> *)listAdapter:(IGListAdapter *)listAdapter sectionControllerForObject:(id)object {
IGListTestSection *list = [[IGListTestSection alloc] init];
return list;
if ([object isKindOfClass:[NSNumber class]]) {
return [IGListTestSection new];
}
return nil;
}
- (nullable UIView *)emptyViewForListAdapter:(IGListAdapter *)listAdapter {