Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add allowsBackgroundReloading Flag to ListAdapterUpdater to Give User Control of Behavior #375

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ This release closes the [2.1.0 milestone](https://github.com/Instagram/IGListKit

- Added CocoaPods subspec for diffing, `IGListKit/Diffing` and an [installation guide](https://instagram.github.io/IGListKit/installation.html). [Sherlouk](https://github.com/Sherlouk) [(#368)](https://github.com/Instagram/IGListKit/pull/368)

- Added `allowsBackgroundReloading` flag (default `YES`) to `IGListAdapterUpdater` so users can configure this behavior as needed. [Adlai-Holler](https://github.com/Adlai-Holler) [(#375)](https://github.com/Instagram/IGListKit/pull/375)

### Fixes

- Avoid `UICollectionView` crashes when queueing a reload and insert/delete on the same item as well as reloading an item in a section that is animating. [Ryan Nystrom](https://github.com/rnystrom) [(#325)](https://github.com/Instagram/IGListKit/pull/325)
Expand Down
11 changes: 11 additions & 0 deletions Source/IGListAdapterUpdater.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ IGLK_SUBCLASSING_RESTRICTED
*/
@property (nonatomic, assign) BOOL movesAsDeletesInserts;

/**
A flag indicating whether this updater should skip diffing and simply call
`reloadData` for updates when the collection view is not in a window. The default value is `YES`.

@note This will result in better performance, but will not generate the same delegate
callbacks. If using a custom layout, it will not receive `prepareForCollectionViewUpdates:`.

@warning On iOS < 8.3, this behavior is unsupported and will always be treated as `NO`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

*/
@property (nonatomic, assign) BOOL allowsBackgroundReloading;

/**
A bitmask of experiments to conduct on the updater.
*/
Expand Down
9 changes: 4 additions & 5 deletions Source/IGListAdapterUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@

#import "UICollectionView+IGListBatchUpdateData.h"

@implementation IGListAdapterUpdater {
BOOL _canBackgroundReload;
}
@implementation IGListAdapterUpdater

- (instancetype)init {
IGAssertMainThread();
Expand All @@ -36,7 +34,7 @@ - (instancetype)init {
_insertIndexPaths = [[NSMutableSet alloc] init];
_reloadIndexPaths = [[NSMutableSet alloc] init];

_canBackgroundReload = [[[UIDevice currentDevice] systemVersion] compare:@"8.3" options:NSNumericSearch] != NSOrderedAscending;
_allowsBackgroundReloading = YES;
}
return self;
}
Expand Down Expand Up @@ -155,7 +153,8 @@ - (void)performBatchUpdatesWithCollectionView:(UICollectionView *)collectionView

// if the collection view isn't in a visible window, skip diffing and batch updating. execute all transition blocks,
// reload data, execute completion blocks, and get outta here
if (_canBackgroundReload && collectionView.window == nil) {
const BOOL iOS83OrLater = (NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_8_3);
if (iOS83OrLater && self.allowsBackgroundReloading && collectionView.window == nil) {
[self beginPerformBatchUpdatestoObjects:toObjects];
executeUpdateBlocks();
[self cleanupUpdateBlockState];
Expand Down
46 changes: 46 additions & 0 deletions Tests/IGListAdapterUpdaterTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -459,4 +459,50 @@ - (void)test_whenCallingReloadData_withUICollectionViewFlowLayout_withEstimatedS
XCTAssertEqual([collectionView numberOfItemsInSection:1], 4);
}

- (void)test_whenCollectionViewNotInWindow_andBackgroundReloadFlag_isSetNO_diffHappens {
self.updater.allowsBackgroundReloading = NO;
[self.collectionView removeFromSuperview];

id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(IGListAdapterUpdaterDelegate)];
self.updater.delegate = mockDelegate;
[mockDelegate setExpectationOrderMatters:YES];
[[mockDelegate expect] listAdapterUpdater:self.updater willPerformBatchUpdatesWithCollectionView:self.collectionView];
[[mockDelegate expect] listAdapterUpdater:self.updater didPerformBatchUpdates:OCMOCK_ANY withCollectionView:self.collectionView];

XCTestExpectation *expectation = genExpectation;
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[]]
];
[self.updater performUpdateWithCollectionView:self.collectionView fromObjects:self.dataSource.sections toObjects:to animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
[mockDelegate verify];
}

- (void)test_whenCollectionViewNotInWindow_andBackgroundReloadFlag_isDefaultYES_diffDoesNotHappen {
[self.collectionView removeFromSuperview];

id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(IGListAdapterUpdaterDelegate)];
self.updater.delegate = mockDelegate;

// NOTE: The current behavior in this case is for the adapter updater
// simply not to call any delegate methods at all. This may change
// in the future, but we configure the mock delegate to allow any call
// except the batch updates calls.

[[mockDelegate reject] listAdapterUpdater:self.updater willPerformBatchUpdatesWithCollectionView:self.collectionView];
[[mockDelegate reject] listAdapterUpdater:self.updater didPerformBatchUpdates:OCMOCK_ANY withCollectionView:self.collectionView];

XCTestExpectation *expectation = genExpectation;
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[]]
];
[self.updater performUpdateWithCollectionView:self.collectionView fromObjects:self.dataSource.sections toObjects:to animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
[mockDelegate verify];
}

@end