mirror of
https://github.com/Instagram/IGListKit
synced 2026-05-24 01:38:26 +00:00
create IGListUpdateTransactionBuilder
Summary: Now, lets move pending changes to a separate object `IGListUpdateTransactionBuilder`, which can we discarded once the update actually starts. Reviewed By: patters Differential Revision: D23145774 fbshipit-source-id: e12de178de33497f476972a9ad89ebb4e8d413ab
This commit is contained in:
parent
8c1253246f
commit
bba6b252a4
4 changed files with 200 additions and 72 deletions
|
|
@ -35,10 +35,7 @@ typedef void (^IGListAdapterUpdaterCompletionBlock)(BOOL);
|
|||
IGAssertMainThread();
|
||||
|
||||
if (self = [super init]) {
|
||||
// the default is to use animations unless NO is passed
|
||||
_queuedUpdateIsAnimated = YES;
|
||||
_completionBlocks = [NSMutableArray new];
|
||||
_itemUpdateBlocks = [NSMutableArray new];
|
||||
_transactionBuilder = [IGListUpdateTransactionBuilder new];
|
||||
_allowsBackgroundReloading = YES;
|
||||
_allowsReloadingOnTooManyUpdates = YES;
|
||||
}
|
||||
|
|
@ -48,18 +45,16 @@ typedef void (^IGListAdapterUpdaterCompletionBlock)(BOOL);
|
|||
#pragma mark - Private API
|
||||
|
||||
- (BOOL)hasChanges {
|
||||
return self.hasQueuedReloadData
|
||||
|| self.itemUpdateBlocks.count > 0
|
||||
|| self.dataBlock != nil;
|
||||
return [self.transactionBuilder hasChanges];
|
||||
}
|
||||
|
||||
- (void)performReloadDataWithCollectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock {
|
||||
IGAssertMainThread();
|
||||
|
||||
id<IGListAdapterUpdaterDelegate> delegate = self.delegate;
|
||||
void (^reloadUpdates)(void) = self.reloadUpdates;
|
||||
NSArray *itemUpdateBlocks = [self.itemUpdateBlocks copy];
|
||||
NSArray *completionBlocks = [self.completionBlocks copy];
|
||||
void (^reloadUpdates)(void) = [self.transactionBuilder reloadBlock];
|
||||
NSArray *itemUpdateBlocks = [self.transactionBuilder itemUpdateBlocks];
|
||||
NSArray *completionBlocks = [self.transactionBuilder completionBlocks];
|
||||
|
||||
[self cleanStateBeforeUpdates];
|
||||
|
||||
|
|
@ -118,13 +113,13 @@ typedef void (^IGListAdapterUpdaterCompletionBlock)(BOOL);
|
|||
|
||||
// create local variables so we can immediately clean our state but pass these items into the batch update block
|
||||
id<IGListAdapterUpdaterDelegate> delegate = self.delegate;
|
||||
IGListTransitionDataBlock dataBlock = [self.dataBlock copy];
|
||||
IGListTransitionDataApplyBlock applyDataBlock = [self.applyDataBlock copy];
|
||||
NSArray *completionBlocks = [self.completionBlocks copy];
|
||||
const BOOL animated = self.queuedUpdateIsAnimated;
|
||||
IGListTransitionDataBlock dataBlock = [self.transactionBuilder dataBlock];
|
||||
IGListTransitionDataApplyBlock applyDataBlock = [self.transactionBuilder applyDataBlock];
|
||||
NSArray *completionBlocks = [self.transactionBuilder completionBlocks];
|
||||
const BOOL animated = [self.transactionBuilder animated];
|
||||
const BOOL allowsReloadingOnTooManyUpdates = self.allowsReloadingOnTooManyUpdates;
|
||||
const IGListExperiment experiments = self.experiments;
|
||||
NSArray *itemUpdateBlocks = [self.itemUpdateBlocks copy];
|
||||
NSArray *itemUpdateBlocks = [self.transactionBuilder itemUpdateBlocks];
|
||||
|
||||
// clean up all state so that new updates can be coalesced while the current update is in flight
|
||||
[self cleanStateBeforeUpdates];
|
||||
|
|
@ -319,23 +314,8 @@ willPerformBatchUpdatesWithCollectionView:collectionView
|
|||
}
|
||||
|
||||
- (void)cleanStateBeforeUpdates {
|
||||
self.queuedUpdateIsAnimated = YES;
|
||||
_transactionBuilder = [IGListUpdateTransactionBuilder new];
|
||||
|
||||
// destroy to/from transition items
|
||||
self.dataBlock = nil;
|
||||
|
||||
// destroy reloadData state
|
||||
self.reloadUpdates = nil;
|
||||
self.queuedReloadData = NO;
|
||||
|
||||
// remove indexpath/item changes
|
||||
self.applyDataBlock = nil;
|
||||
|
||||
// removes all object completion blocks. done before updates to start collecting completion blocks for coalesced
|
||||
// or re-entrant object updates
|
||||
[self.completionBlocks removeAllObjects];
|
||||
|
||||
[self.itemUpdateBlocks removeAllObjects];
|
||||
self.inUpdateCompletionBlocks = [NSMutableArray new];
|
||||
self.inUpdateItemCollector = [IGListItemUpdatesCollector new];
|
||||
}
|
||||
|
|
@ -360,7 +340,7 @@ willPerformBatchUpdatesWithCollectionView:collectionView
|
|||
return;
|
||||
}
|
||||
|
||||
if (weakSelf.hasQueuedReloadData) {
|
||||
if (weakSelf.transactionBuilder.hasReloadData) {
|
||||
[weakSelf performReloadDataWithCollectionViewBlock:collectionViewBlock];
|
||||
} else {
|
||||
[weakSelf performBatchUpdatesWithCollectionViewBlock:collectionViewBlock];
|
||||
|
|
@ -410,20 +390,11 @@ static NSUInteger IGListIdentifierHash(const void *item, NSUInteger (*size)(cons
|
|||
IGParameterAssert(dataBlock != nil);
|
||||
IGParameterAssert(applyDataBlock != nil);
|
||||
|
||||
// will call the dataBlock after the dispatch
|
||||
self.dataBlock = dataBlock;
|
||||
|
||||
// always use the last update block, even though this should always do the exact same thing
|
||||
self.applyDataBlock = applyDataBlock;
|
||||
|
||||
// disabled animations will always take priority
|
||||
// reset to YES in -cleanupState
|
||||
self.queuedUpdateIsAnimated = self.queuedUpdateIsAnimated && animated;
|
||||
|
||||
IGListUpdatingCompletion localCompletion = completion;
|
||||
if (localCompletion) {
|
||||
[self.completionBlocks addObject:localCompletion];
|
||||
}
|
||||
[self.transactionBuilder addSectionBatchUpdateAnimated:animated
|
||||
collectionViewBlock:collectionViewBlock
|
||||
dataBlock:dataBlock
|
||||
applyDataBlock:applyDataBlock
|
||||
completion:completion];
|
||||
|
||||
[self _queueUpdateWithCollectionViewBlock:collectionViewBlock];
|
||||
}
|
||||
|
|
@ -445,15 +416,10 @@ static NSUInteger IGListIdentifierHash(const void *item, NSUInteger (*size)(cons
|
|||
}
|
||||
itemUpdates();
|
||||
} else {
|
||||
if (completion != nil) {
|
||||
[self.completionBlocks addObject:completion];
|
||||
}
|
||||
|
||||
[self.itemUpdateBlocks addObject:itemUpdates];
|
||||
|
||||
// disabled animations will always take priority
|
||||
// reset to YES in -cleanupState
|
||||
self.queuedUpdateIsAnimated = self.queuedUpdateIsAnimated && animated;
|
||||
[self.transactionBuilder addItemBatchUpdateAnimated:animated
|
||||
collectionViewBlock:collectionViewBlock
|
||||
itemUpdates:itemUpdates
|
||||
completion:completion];
|
||||
|
||||
[self _queueUpdateWithCollectionViewBlock:collectionViewBlock];
|
||||
}
|
||||
|
|
@ -554,13 +520,10 @@ static NSUInteger IGListIdentifierHash(const void *item, NSUInteger (*size)(cons
|
|||
IGParameterAssert(collectionViewBlock != nil);
|
||||
IGParameterAssert(reloadUpdateBlock != nil);
|
||||
|
||||
IGListUpdatingCompletion localCompletion = completion;
|
||||
if (localCompletion) {
|
||||
[self.completionBlocks addObject:localCompletion];
|
||||
}
|
||||
[self.transactionBuilder addReloadDataWithCollectionViewBlock:collectionViewBlock
|
||||
reloadBlock:reloadUpdateBlock
|
||||
completion:completion];
|
||||
|
||||
self.reloadUpdates = reloadUpdateBlock;
|
||||
self.queuedReloadData = YES;
|
||||
[self _queueUpdateWithCollectionViewBlock:collectionViewBlock];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,26 +14,17 @@
|
|||
#import "IGListExperimentalAdapterUpdater.h"
|
||||
#import "IGListBatchUpdateState.h"
|
||||
#import "IGListItemUpdatesCollector.h"
|
||||
#import "IGListUpdateTransactionBuilder.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface IGListExperimentalAdapterUpdater ()
|
||||
|
||||
@property (nonatomic, copy, nullable) IGListTransitionDataBlock dataBlock;
|
||||
@property (nonatomic, strong) NSMutableArray<IGListUpdatingCompletion> *completionBlocks;
|
||||
|
||||
@property (nonatomic, assign) BOOL queuedUpdateIsAnimated;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray<IGListItemUpdateBlock> *itemUpdateBlocks;
|
||||
@property (nonatomic, strong, readonly) IGListUpdateTransactionBuilder *transactionBuilder;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray<IGListUpdatingCompletion> *inUpdateCompletionBlocks;
|
||||
@property (nonatomic, strong) IGListItemUpdatesCollector *inUpdateItemCollector;
|
||||
|
||||
@property (nonatomic, copy, nullable) IGListTransitionDataApplyBlock applyDataBlock;
|
||||
|
||||
@property (nonatomic, copy, nullable) IGListReloadUpdateBlock reloadUpdates;
|
||||
@property (nonatomic, assign, getter=hasQueuedReloadData) BOOL queuedReloadData;
|
||||
|
||||
@property (nonatomic, assign) IGListBatchUpdateState state;
|
||||
@property (nonatomic, strong, nullable) IGListBatchUpdateData *applyingUpdateData;
|
||||
|
||||
|
|
|
|||
79
Source/IGListKit/Internal/IGListUpdateTransactionBuilder.h
Normal file
79
Source/IGListKit/Internal/IGListUpdateTransactionBuilder.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <IGListDiffKit/IGListMacros.h>
|
||||
#import <IGListKit/IGListUpdatingDelegate.h>
|
||||
#import <IGListKit/IGListUpdatingDelegateExperimental.h>
|
||||
|
||||
@protocol IGListAdapterUpdaterDelegate;
|
||||
@protocol IGListAdapterUpdating;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Class to collect reload & update information before actually starting the transition.
|
||||
IGLK_SUBCLASSING_RESTRICTED
|
||||
@interface IGListUpdateTransactionBuilder : NSObject
|
||||
|
||||
/**
|
||||
Add a section-level update.
|
||||
|
||||
@param animated A flag indicating if the transition should be animated.
|
||||
@param collectionViewBlock A block returning the collecion view to perform updates on.
|
||||
@param dataBlock A block which returns the transition data
|
||||
@param applyDataBlock A block that applies the data passed from the `dataBlock` block
|
||||
@param completion A completion block to execute when the update is finished.
|
||||
*/
|
||||
- (void)addSectionBatchUpdateAnimated:(BOOL)animated
|
||||
collectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
|
||||
dataBlock:(IGListTransitionDataBlock)dataBlock
|
||||
applyDataBlock:(IGListTransitionDataApplyBlock)applyDataBlock
|
||||
completion:(nullable IGListUpdatingCompletion)completion;
|
||||
|
||||
/**
|
||||
Add a item-level update.
|
||||
|
||||
@param animated A flag indicating if the transition should be animated.
|
||||
@param collectionViewBlock A block returning the collecion view to perform updates on.
|
||||
@param itemUpdates A block containing all of the updates.
|
||||
@param completion A completion block to execute when the update is finished.
|
||||
*/
|
||||
- (void)addItemBatchUpdateAnimated:(BOOL)animated
|
||||
collectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
|
||||
itemUpdates:(IGListItemUpdateBlock)itemUpdates
|
||||
completion:(nullable IGListUpdatingCompletion)completion;
|
||||
|
||||
/**
|
||||
Completely reload data in the collection.
|
||||
|
||||
@param collectionViewBlock A block returning the collecion view to reload.
|
||||
@param reloadBlock A block that must be called when the adapter reloads the collection view.
|
||||
@param completion A completion block to execute when the reload is finished.
|
||||
*/
|
||||
- (void)addReloadDataWithCollectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
|
||||
reloadBlock:(IGListReloadUpdateBlock)reloadBlock
|
||||
completion:(nullable IGListUpdatingCompletion)completion;
|
||||
|
||||
// Batch updates
|
||||
- (nullable IGListTransitionDataBlock)dataBlock;
|
||||
- (nullable IGListTransitionDataApplyBlock)applyDataBlock;
|
||||
- (NSMutableArray<IGListItemUpdateBlock> *)itemUpdateBlocks;
|
||||
- (BOOL)animated;
|
||||
|
||||
// Reload
|
||||
- (BOOL)hasReloadData;
|
||||
- (nullable IGListReloadUpdateBlock)reloadBlock;
|
||||
|
||||
// Both
|
||||
- (nullable IGListCollectionViewBlock)collectionViewBlock;
|
||||
- (NSMutableArray<IGListUpdatingCompletion> *)completionBlocks;
|
||||
- (BOOL)hasChanges;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
95
Source/IGListKit/Internal/IGListUpdateTransactionBuilder.m
Normal file
95
Source/IGListKit/Internal/IGListUpdateTransactionBuilder.m
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "IGListUpdateTransactionBuilder.h"
|
||||
|
||||
@interface IGListUpdateTransactionBuilder ()
|
||||
// Batch updates
|
||||
@property (nonatomic, copy, readwrite, nullable) IGListTransitionDataBlock dataBlock;
|
||||
@property (nonatomic, copy, readwrite, nullable) IGListTransitionDataApplyBlock applyDataBlock;
|
||||
@property (nonatomic, strong, readonly) NSMutableArray<IGListItemUpdateBlock> *itemUpdateBlocks;
|
||||
@property (nonatomic, assign, readwrite) BOOL animated;
|
||||
// Reload
|
||||
@property (nonatomic, assign, readwrite) BOOL hasReloadData;
|
||||
@property (nonatomic, copy, readwrite, nullable) IGListReloadUpdateBlock reloadBlock;
|
||||
// Both
|
||||
@property (nonatomic, copy, readwrite, nullable) IGListCollectionViewBlock collectionViewBlock;
|
||||
@property (nonatomic, strong, readonly) NSMutableArray<IGListUpdatingCompletion> *completionBlocks;
|
||||
@end
|
||||
|
||||
@implementation IGListUpdateTransactionBuilder
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_animated = YES;
|
||||
_itemUpdateBlocks = [NSMutableArray new];
|
||||
_completionBlocks = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Add changes
|
||||
|
||||
- (void)addSectionBatchUpdateAnimated:(BOOL)animated
|
||||
collectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
|
||||
dataBlock:(IGListTransitionDataBlock)dataBlock
|
||||
applyDataBlock:(IGListTransitionDataApplyBlock)applyDataBlock
|
||||
completion:(IGListUpdatingCompletion)completion {
|
||||
// disabled animations will always take priority
|
||||
// reset to YES in -cleanupState
|
||||
self.animated = self.animated && animated;
|
||||
self.collectionViewBlock = collectionViewBlock;
|
||||
|
||||
// will call the dataBlock after the dispatch
|
||||
self.dataBlock = dataBlock;
|
||||
|
||||
// always use the last update block, even though this should always do the exact same thing
|
||||
self.applyDataBlock = applyDataBlock;
|
||||
|
||||
IGListUpdatingCompletion localCompletion = completion;
|
||||
if (localCompletion) {
|
||||
[self.completionBlocks addObject:localCompletion];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addItemBatchUpdateAnimated:(BOOL)animated
|
||||
collectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
|
||||
itemUpdates:(IGListItemUpdateBlock)itemUpdates
|
||||
completion:(nullable IGListUpdatingCompletion)completion {
|
||||
// disabled animations will always take priority
|
||||
// reset to YES in -cleanupState
|
||||
self.animated = self.animated && animated;
|
||||
self.collectionViewBlock = collectionViewBlock;
|
||||
|
||||
[self.itemUpdateBlocks addObject:itemUpdates];
|
||||
|
||||
IGListUpdatingCompletion localCompletion = completion;
|
||||
if (localCompletion) {
|
||||
[self.completionBlocks addObject:localCompletion];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addReloadDataWithCollectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
|
||||
reloadBlock:(IGListReloadUpdateBlock)reloadBlock
|
||||
completion:(nullable IGListUpdatingCompletion)completion {
|
||||
self.hasReloadData = YES;
|
||||
self.collectionViewBlock = collectionViewBlock;
|
||||
self.reloadBlock = reloadBlock;
|
||||
|
||||
IGListUpdatingCompletion localCompletion = completion;
|
||||
if (localCompletion) {
|
||||
[self.completionBlocks addObject:localCompletion];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hasChanges {
|
||||
return self.hasReloadData
|
||||
|| self.itemUpdateBlocks.count > 0
|
||||
|| self.dataBlock != nil;
|
||||
}
|
||||
|
||||
@end
|
||||
Loading…
Reference in a new issue