Skip to content

Commit

Permalink
merge IGListUpdatingDelegateExperimental into IGListUpdatingDelegate
Browse files Browse the repository at this point in the history
Summary:
Now that the new updater has shipped, lets update `IGListUpdatingDelegate` with the new methods:
* `-performExperimentalUpdateAnimated` is the new section update method (renaming coming in the next diff)
* `-performDataSourceChange` lets us safely update the `UICollectionView` dataSource

Also, something I've been wanting to do for a long time, lets group related methods in `IGListUpdatingDelegate.h`.

Reviewed By: Haud

Differential Revision: D25884780

fbshipit-source-id: 5d9201ace8bf6b281d71ff03463cb7c911e7f967
  • Loading branch information
maxolls authored and facebook-github-bot committed Jan 22, 2021
1 parent 247e7ca commit 43af883
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 239 deletions.
162 changes: 45 additions & 117 deletions Source/IGListKit/IGListAdapter.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#import "IGListDebugger.h"
#import "IGListSectionControllerInternal.h"
#import "IGListTransitionData.h"
#import "IGListUpdatingDelegateExperimental.h"
#import "IGListUpdatingDelegate.h"
#import "UICollectionViewLayout+InteractiveReordering.h"
#import "UIScrollView+IGListKit.h"

Expand All @@ -24,8 +24,6 @@ @implementation IGListAdapter {
// An array of blocks to execute once batch updates are finished
NSMutableArray<void (^)(void)> *_queuedCompletionBlocks;
NSHashTable<id<IGListAdapterUpdateListener>> *_updateListeners;
// Temporary property while we experiment with a new updater.
id<IGListUpdatingDelegateExperimental> _experimentalUpdater;
}

- (void)dealloc {
Expand Down Expand Up @@ -56,7 +54,6 @@ - (instancetype)initWithUpdater:(id <IGListUpdatingDelegate>)updater

_updater = updater;
_viewController = viewController;
_experimentalUpdater = [updater conformsToProtocol:@protocol(IGListUpdatingDelegateExperimental)] ? (id<IGListUpdatingDelegateExperimental>)updater : nil;

[IGListDebugger trackAdapter:self];
}
Expand Down Expand Up @@ -97,46 +94,33 @@ - (void)setCollectionView:(UICollectionView *)collectionView {
_registeredSupplementaryViewIdentifiers = [NSMutableSet new];
_registeredSupplementaryViewNibNames = [NSMutableSet new];

const BOOL settingFirstCollectionView = _collectionView == nil;

if (_experimentalUpdater) {
// We can't just swap out the collectionView, because we might have on-going or pending updates.
// `_experimentalUpdater` can take care of that by wrapping the change in `performDataSourceChange`.

[_experimentalUpdater performDataSourceChange:^{
if (self->_collectionView.dataSource == self) {
// Since we're not going to sync the previous collectionView anymore, lets not be its dataSource.
self->_collectionView.dataSource = nil;
}
self->_collectionView = collectionView;
self->_collectionView.dataSource = self;
// We can't just swap out the collectionView, because we might have on-going or pending updates.
// `_updater` can take care of that by wrapping the change in `performDataSourceChange`.
[_updater performDataSourceChange:^{
if (self->_collectionView.dataSource == self) {
// Since we're not going to sync the previous collectionView anymore, lets not be its dataSource.
self->_collectionView.dataSource = nil;
}
self->_collectionView = collectionView;
self->_collectionView.dataSource = self;

[self _updateCollectionViewDelegate];
[self _updateCollectionViewDelegate];

// Sync the dataSource <> adapter for a couple of reasons:
// 1. We might not have synced on -setDataSource, so now is the time to try again.
// 2. Any in-flight `performUpdatesAnimated` were cancelled, so lets make sure we have the latest data.
[self _updateObjects];
// Sync the dataSource <> adapter for a couple of reasons:
// 1. We might not have synced on -setDataSource, so now is the time to try again.
// 2. Any in-flight `performUpdatesAnimated` were cancelled, so lets make sure we have the latest data.
[self _updateObjects];

// The sync between the collectionView <> adapter will happen automically, since
// we just changed the `collectionView.dataSource`.
}];
} else {
_collectionView = collectionView;
_collectionView.dataSource = self;
[self _updateCollectionViewDelegate];
}
// The sync between the collectionView <> adapter will happen automically, since
// we just changed the `collectionView.dataSource`.
}];

if (@available(iOS 10.0, tvOS 10, *)) {
_collectionView.prefetchingEnabled = NO;
}

[_collectionView.collectionViewLayout ig_hijackLayoutInteractiveReorderingMethodForAdapter:self];
[_collectionView.collectionViewLayout invalidateLayout];

if (!_experimentalUpdater && settingFirstCollectionView) {
[self _updateObjectsIfHasDataSource];
}
}
}

Expand All @@ -145,24 +129,19 @@ - (void)setDataSource:(id<IGListAdapterDataSource>)dataSource {
return;
}

if (_experimentalUpdater) {
[_experimentalUpdater performDataSourceChange:^{
self->_dataSource = dataSource;
[_updater performDataSourceChange:^{
self->_dataSource = dataSource;

// Invalidate the collectionView internal section & item counts, as if its dataSource changed.
self->_collectionView.dataSource = nil;
self->_collectionView.dataSource = self;
// Invalidate the collectionView internal section & item counts, as if its dataSource changed.
self->_collectionView.dataSource = nil;
self->_collectionView.dataSource = self;

// Sync the dataSource <> adapter
[self _updateObjects];
// Sync the dataSource <> adapter
[self _updateObjects];

// The sync between the collectionView <> adapter will happen automically, since
// we just changed the `collectionView.dataSource`.
}];
} else {
_dataSource = dataSource;
[self _updateObjectsIfHasDataSource];
}
// The sync between the collectionView <> adapter will happen automically, since
// we just changed the `collectionView.dataSource`.
}];
}

// reset and configure the delegate proxy whenever this property is set
Expand All @@ -186,13 +165,6 @@ - (void)setScrollViewDelegate:(id<UIScrollViewDelegate>)scrollViewDelegate {
}
}

- (void)_updateObjectsIfHasDataSource {
// This is to keep the existing logic while testing `experimentalUpdater`
if (_dataSource != nil) {
[self _updateObjects];
}
}

- (void)_updateObjects {
if (_collectionView == nil) {
// If we don't have a collectionView, we can't do much.
Expand Down Expand Up @@ -375,66 +347,8 @@ - (void)performUpdatesAnimated:(BOOL)animated completion:(IGListUpdaterCompletio
return;
}

__weak __typeof__(self) weakSelf = self;
IGListUpdaterCompletion outerCompletionBlock = ^(BOOL finished){
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
IGLK_BLOCK_CALL_SAFE(completion,finished);
return;
}

// release the previous items
strongSelf.previousSectionMap = nil;
[strongSelf _notifyDidUpdate:IGListAdapterUpdateTypePerformUpdates animated:animated];
IGLK_BLOCK_CALL_SAFE(completion,finished);
[strongSelf _exitBatchUpdates];
};

[self _enterBatchUpdates];
if (_experimentalUpdater) {
[self _performExperimentalUpdatesWithUpdater:_experimentalUpdater
dataSource:dataSource
animated:animated
completion:outerCompletionBlock];
} else {
[self _performRegularUpdatesWithUpdater:updater
dataSource:dataSource
animated:animated
completion:outerCompletionBlock];
}
}

- (void)_performRegularUpdatesWithUpdater:(id<IGListUpdatingDelegate>)updater
dataSource:(id<IGListAdapterDataSource>)dataSource
animated:(BOOL)animated
completion:(IGListUpdaterCompletion)completion {
NSArray *fromObjects = self.sectionMap.objects;

__weak __typeof__(self) weakSelf = self;
IGListToObjectBlock toObjectsBlock = ^NSArray *{
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
return nil;
}
return [dataSource objectsForListAdapter:strongSelf];
};

[updater performUpdateWithCollectionViewBlock:[self _collectionViewBlock]
fromObjects:fromObjects
toObjectsBlock:toObjectsBlock
animated:animated
objectTransitionBlock:^(NSArray *toObjects) {
// temporarily capture the item map that we are transitioning from in case
// there are any item deletes at the same
weakSelf.previousSectionMap = [weakSelf.sectionMap copy];
[weakSelf _updateObjects:toObjects dataSource:dataSource];
} completion:completion];
}

- (void)_performExperimentalUpdatesWithUpdater:(id<IGListUpdatingDelegateExperimental>)updater
dataSource:(id<IGListAdapterDataSource>)dataSource
animated:(BOOL)animated
completion:(IGListUpdaterCompletion)completion {
__weak __typeof__(self) weakSelf = self;
IGListTransitionDataBlock dataBlock = ^IGListTransitionData *{
__typeof__(self) strongSelf = weakSelf;
Expand All @@ -452,15 +366,29 @@ - (void)_performExperimentalUpdatesWithUpdater:(id<IGListUpdatingDelegateExperim
}
// temporarily capture the item map that we are transitioning from in case
// there are any item deletes at the same
strongSelf.previousSectionMap = [weakSelf.sectionMap copy];
strongSelf.previousSectionMap = [strongSelf.sectionMap copy];
[strongSelf _updateWithData:data];
};

IGListUpdaterCompletion outerCompletionBlock = ^(BOOL finished){
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
IGLK_BLOCK_CALL_SAFE(completion,finished);
return;
}

// release the previous items
strongSelf.previousSectionMap = nil;
[strongSelf _notifyDidUpdate:IGListAdapterUpdateTypePerformUpdates animated:animated];
IGLK_BLOCK_CALL_SAFE(completion,finished);
[strongSelf _exitBatchUpdates];
};

[updater performExperimentalUpdateAnimated:animated
collectionViewBlock:[self _collectionViewBlock]
dataBlock:dataBlock
applyDataBlock:applyDataBlock
completion:completion];
completion:outerCompletionBlock];
}

- (void)reloadDataWithCompletion:(nullable IGListUpdaterCompletion)completion {
Expand Down Expand Up @@ -795,7 +723,7 @@ - (void)_updateBackgroundViewShouldHide:(BOOL)shouldHide {
return; // will be called again when update block completes
}

if (!shouldHide || !_experimentalUpdater) {
if (!shouldHide) {
UIView *backgroundView = [self.dataSource emptyViewForListAdapter:self];
// don't do anything if the client is using the same view
if (backgroundView != _collectionView.backgroundView) {
Expand Down
3 changes: 1 addition & 2 deletions Source/IGListKit/IGListAdapterUpdater.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

#import <IGListDiffKit/IGListMacros.h>
#import <IGListKit/IGListAdapterUpdaterCompatible.h>
#import <IGListKit/IGListUpdatingDelegateExperimental.h>

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -23,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
IGLK_SUBCLASSING_RESTRICTED
NS_SWIFT_NAME(ListAdapterUpdater)
@interface IGListAdapterUpdater : NSObject <IGListAdapterUpdaterCompatible, IGListUpdatingDelegateExperimental>
@interface IGListAdapterUpdater : NSObject <IGListAdapterUpdaterCompatible>

@end

Expand Down
10 changes: 0 additions & 10 deletions Source/IGListKit/IGListAdapterUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,6 @@ - (NSPointerFunctions *)objectLookupPointerFunctions {
return functions;
}

- (void)performUpdateWithCollectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
fromObjects:(NSArray *)fromObjects
toObjectsBlock:(IGListToObjectBlock)toObjectsBlock
animated:(BOOL)animated
objectTransitionBlock:(IGListObjectTransitionBlock)objectTransitionBlock
completion:(IGListUpdatingCompletion)completion {
IGFailAssert(@"IGListExperimentalAdapterUpdater works with IGListUpdatingDelegateExperimental and doesn't implement the regular -performUpdateWithCollectionViewBlock method");
completion(NO);
}

- (void)performExperimentalUpdateAnimated:(BOOL)animated
collectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
dataBlock:(IGListTransitionDataBlock)dataBlock
Expand Down
39 changes: 22 additions & 17 deletions Source/IGListKit/IGListReloadDataUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ - (NSPointerFunctions *)objectLookupPointerFunctions {
return [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsObjectPersonality];
}

- (void)performUpdateWithCollectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
fromObjects:(NSArray *)fromObjects
toObjectsBlock:(IGListToObjectBlock)toObjectsBlock
animated:(BOOL)animated
objectTransitionBlock:(IGListObjectTransitionBlock)objectTransitionBlock
completion:(IGListUpdatingCompletion)completion {
if (toObjectsBlock != nil) {
NSArray *toObjects = toObjectsBlock() ?: @[];
objectTransitionBlock(toObjects);
- (void)performExperimentalUpdateAnimated:(BOOL)animated
collectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
dataBlock:(IGListTransitionDataBlock)dataBlock
applyDataBlock:(IGListTransitionDataApplyBlock)applyDataBlock
completion:(nullable IGListUpdatingCompletion)completion {
IGListTransitionData *sectionData = dataBlock ? dataBlock() : nil;
if (sectionData != nil) {
applyDataBlock(sectionData);
}
[self _synchronousReloadDataWithCollectionView:collectionViewBlock()];
if (completion) {
Expand All @@ -42,6 +41,20 @@ - (void)performUpdateWithCollectionViewBlock:(IGListCollectionViewBlock)collecti
}
}

- (void)performDataSourceChange:(IGListDataSourceChangeBlock)block {
// A `UICollectionView` dataSource change will automatically invalidate
// its data, so no need to do anything else.
block();
}

- (void)reloadDataWithCollectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock reloadUpdateBlock:(IGListReloadUpdateBlock)reloadUpdateBlock completion:(IGListUpdatingCompletion)completion {
reloadUpdateBlock();
[self _synchronousReloadDataWithCollectionView:collectionViewBlock()];
if (completion) {
completion(YES);
}
}

- (void)insertItemsIntoCollectionView:(UICollectionView *)collectionView indexPaths:(NSArray<NSIndexPath *> *)indexPaths {
[self _synchronousReloadDataWithCollectionView:collectionView];
}
Expand All @@ -62,14 +75,6 @@ - (void)moveSectionInCollectionView:(UICollectionView *)collectionView fromIndex
[self _synchronousReloadDataWithCollectionView:collectionView];
}

- (void)reloadDataWithCollectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock reloadUpdateBlock:(IGListReloadUpdateBlock)reloadUpdateBlock completion:(IGListUpdatingCompletion)completion {
reloadUpdateBlock();
[self _synchronousReloadDataWithCollectionView:collectionViewBlock()];
if (completion) {
completion(YES);
}
}

- (void)reloadCollectionView:(UICollectionView *)collectionView sections:(NSIndexSet *)sections {
[self _synchronousReloadDataWithCollectionView:collectionView];
}
Expand Down
Loading

0 comments on commit 43af883

Please sign in to comment.