implement performExperimentalUpdateAnimated

Summary:
Lets implement `performExperimentalUpdateAnimated`. There's a couple of things happening:
* We're now using the `IGListTransitionData` container object
* We don't need `pendingTransitionToObjects` anymore because we query the `fromObjects` just before performing the diffing.
* We should update the unit tests to use `performExperimentalUpdateAnimated` since the regular `performUpdateAnimated` will now fail.

Reviewed By: patters

Differential Revision: D23145781

fbshipit-source-id: 0b896e918241e709c5eb23f56a6b7323521a2222
This commit is contained in:
Maxime Ollivier 2020-09-08 09:06:16 -07:00 committed by Facebook GitHub Bot
parent 088d932707
commit caf058c85a
5 changed files with 260 additions and 187 deletions

View file

@ -393,7 +393,7 @@
if (strongSelf == nil) {
return nil;
}
NSArray *toObjects = [dataSource objectsForListAdapter:strongSelf];
NSArray *toObjects = objectsWithDuplicateIdentifiersRemoved([dataSource objectsForListAdapter:strongSelf]);
return [strongSelf _generateTransitionDataWithObjects:toObjects dataSource:dataSource];
};

View file

@ -9,6 +9,7 @@
#import <IGListDiffKit/IGListMacros.h>
#import <IGListKit/IGListAdapterUpdaterCompatible.h>
#import <IGListKit/IGListUpdatingDelegateExperimental.h>
NS_ASSUME_NONNULL_BEGIN
@ -24,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
IGLK_SUBCLASSING_RESTRICTED
NS_SWIFT_NAME(ListExperimentalAdapterUpdater)
@interface IGListExperimentalAdapterUpdater : NSObject <IGListAdapterUpdaterCompatible>
@interface IGListExperimentalAdapterUpdater : NSObject <IGListAdapterUpdaterCompatible, IGListUpdatingDelegateExperimental>
@end

View file

@ -11,10 +11,10 @@
#import <IGListDiffKit/IGListAssert.h>
#import "IGListAdapterUpdaterHelpers.h"
#import "IGListArrayUtilsInternal.h"
#import "IGListIndexSetResultInternal.h"
#import "IGListMoveIndexPathInternal.h"
#import "IGListReloadIndexPath.h"
#import "IGListTransitionData.h"
#import "UICollectionView+IGListBatchUpdateData.h"
typedef void (^IGListAdapterUpdaterDiffResultBlock)(IGListIndexSetResult *);
@ -50,8 +50,7 @@ typedef void (^IGListAdapterUpdaterCompletionBlock)(BOOL);
- (BOOL)hasChanges {
return self.hasQueuedReloadData
|| [self.batchUpdates hasChanges]
|| self.fromObjects != nil
|| self.toObjectsBlock != nil;
|| self.dataBlock != nil;
}
- (void)performReloadDataWithCollectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock {
@ -118,10 +117,9 @@ 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;
NSArray *fromObjects = [self.fromObjects copy];
IGListToObjectBlock toObjectsBlock = [self.toObjectsBlock copy];
IGListTransitionDataBlock dataBlock = [self.dataBlock copy];
IGListTransitionDataApplyBlock applyDataBlock = [self.applyDataBlock copy];
NSMutableArray *completionBlocks = [self.completionBlocks mutableCopy];
void (^objectTransitionBlock)(NSArray *) = [self.objectTransitionBlock copy];
const BOOL animated = self.queuedUpdateIsAnimated;
const BOOL allowsReloadingOnTooManyUpdates = self.allowsReloadingOnTooManyUpdates;
const IGListExperiment experiments = self.experiments;
@ -148,10 +146,15 @@ typedef void (^IGListAdapterUpdaterCompletionBlock)(BOOL);
return;
}
NSArray *toObjects = nil;
if (toObjectsBlock != nil) {
toObjects = objectsWithDuplicateIdentifiersRemoved(toObjectsBlock());
IGListTransitionData *data = nil;
if (dataBlock != nil) {
data = dataBlock();
}
NSArray *toObjects = data.toObjects;
NSArray *fromObjects = data.fromObjects;
#ifdef DEBUG
for (id obj in toObjects) {
IGAssert([obj conformsToProtocol:@protocol(IGListDiffable)],
@ -167,8 +170,8 @@ typedef void (^IGListAdapterUpdaterCompletionBlock)(BOOL);
// run the update block so that the adapter can set its items. this makes sure that just before the update is
// committed that the data source is updated to the /latest/ "toObjects". this makes the data source in sync
// with the items that the updater is transitioning to
if (objectTransitionBlock != nil) {
objectTransitionBlock(toObjects);
if (applyDataBlock != nil && data != nil) {
applyDataBlock(data);
}
// execute each item update block which should make calls like insert, delete, and reload for index paths
@ -189,7 +192,6 @@ typedef void (^IGListAdapterUpdaterCompletionBlock)(BOOL);
[delegate listAdapterUpdater:self willReloadDataWithCollectionView:collectionView isFallbackReload:YES];
executeUpdateBlocks();
[self _cleanStateAfterUpdates];
[self _performBatchUpdatesItemBlockApplied];
[collectionView reloadData];
[collectionView layoutIfNeeded];
executeCompletionBlocks(YES);
@ -201,7 +203,7 @@ typedef void (^IGListAdapterUpdaterCompletionBlock)(BOOL);
};
// disables multiple performBatchUpdates: from happening at the same time
[self _beginPerformBatchUpdatesToObjects:toObjects];
self.state = IGListBatchUpdateStateQueuedBatchUpdate;
// 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
@ -239,7 +241,6 @@ typedef void (^IGListAdapterUpdaterCompletionBlock)(BOOL);
}
[self _cleanStateAfterUpdates];
[self _performBatchUpdatesItemBlockApplied];
};
// block used as the second param of -[UICollectionView performBatchUpdates:completion:]
@ -312,28 +313,18 @@ willPerformBatchUpdatesWithCollectionView:collectionView
});
}
- (void)_beginPerformBatchUpdatesToObjects:(NSArray *)toObjects {
self.pendingTransitionToObjects = toObjects;
self.state = IGListBatchUpdateStateQueuedBatchUpdate;
}
- (void)_performBatchUpdatesItemBlockApplied {
self.pendingTransitionToObjects = nil;
}
- (void)cleanStateBeforeUpdates {
self.queuedUpdateIsAnimated = YES;
// destroy to/from transition items
self.fromObjects = nil;
self.toObjectsBlock = nil;
self.dataBlock = nil;
// destroy reloadData state
self.reloadUpdates = nil;
self.queuedReloadData = NO;
// remove indexpath/item changes
self.objectTransitionBlock = nil;
self.applyDataBlock = nil;
// removes all object completion blocks. done before updates to start collecting completion blocks for coalesced
// or re-entrant object updates
@ -395,25 +386,30 @@ static NSUInteger IGListIdentifierHash(const void *item, NSUInteger (*size)(cons
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
applyDataBlock:(IGListTransitionDataApplyBlock)applyDataBlock
completion:(IGListUpdatingCompletion)completion {
IGAssertMainThread();
IGParameterAssert(collectionViewBlock != nil);
IGParameterAssert(objectTransitionBlock != nil);
IGParameterAssert(dataBlock != nil);
IGParameterAssert(applyDataBlock != nil);
// only update the items that we are coming from if it has not been set
// this allows multiple updates to be called while an update is already in progress, and the transition from > to
// will be done on the first "fromObjects" received and the last "toObjects"
// if performBatchUpdates: hasn't applied the update block, then data source hasn't transitioned its state. if an
// update is queued in between then we must use the pending toObjects
self.fromObjects = self.fromObjects ?: self.pendingTransitionToObjects ?: fromObjects;
self.toObjectsBlock = toObjectsBlock;
// 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;
// always use the last update block, even though this should always do the exact same thing
self.objectTransitionBlock = objectTransitionBlock;
IGListUpdatingCompletion localCompletion = completion;
if (localCompletion) {
[self.completionBlocks addObject:localCompletion];
@ -422,6 +418,7 @@ static NSUInteger IGListIdentifierHash(const void *item, NSUInteger (*size)(cons
[self _queueUpdateWithCollectionViewBlock:collectionViewBlock];
}
- (void)performUpdateWithCollectionViewBlock:(IGListCollectionViewBlock)collectionViewBlock
animated:(BOOL)animated
itemUpdates:(void (^)(void))itemUpdates

View file

@ -9,6 +9,7 @@
#import <QuartzCore/QuartzCore.h>
#import <IGListDiffKit/IGListMoveIndexPath.h>
#import <IGListKit/IGListUpdatingDelegateExperimental.h>
#import "IGListExperimentalAdapterUpdater.h"
#import "IGListBatchUpdateState.h"
@ -18,16 +19,14 @@ NS_ASSUME_NONNULL_BEGIN
@interface IGListExperimentalAdapterUpdater ()
@property (nonatomic, copy, nullable) NSArray *fromObjects;
@property (nonatomic, copy, nullable) IGListToObjectBlock toObjectsBlock;
@property (nonatomic, copy, nullable) NSArray *pendingTransitionToObjects;
@property (nonatomic, copy, nullable) IGListTransitionDataBlock dataBlock;
@property (nonatomic, strong) NSMutableArray<IGListUpdatingCompletion> *completionBlocks;
@property (nonatomic, assign) BOOL queuedUpdateIsAnimated;
@property (nonatomic, strong) IGListBatchUpdates *batchUpdates;
@property (nonatomic, copy, nullable) IGListObjectTransitionBlock objectTransitionBlock;
@property (nonatomic, copy, nullable) IGListTransitionDataApplyBlock applyDataBlock;
@property (nonatomic, copy, nullable) IGListReloadUpdateBlock reloadUpdates;
@property (nonatomic, assign, getter=hasQueuedReloadData) BOOL queuedReloadData;

View file

@ -15,6 +15,7 @@
#import "IGListExperimentalAdapterUpdaterInternal.h"
#import "IGListMoveIndexInternal.h"
#import "IGListTestUICollectionViewDataSource.h"
#import "IGListTransitionData.h"
#import "IGTestObject.h"
#define genExpectation [self expectationWithDescription:NSStringFromSelector(_cmd)]
@ -27,7 +28,7 @@
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) IGListTestUICollectionViewDataSource *dataSource;
@property (nonatomic, strong) IGListExperimentalAdapterUpdater *updater;
@property (nonatomic, strong) IGListObjectTransitionBlock updateBlock;
@property (nonatomic, strong) IGListTransitionDataApplyBlock applyDataBlock;
@end
@ -37,6 +38,12 @@
return ^UICollectionView *{ return self.collectionView; };
}
- (IGListTransitionDataBlock)dataBlockFromObjects:(NSArray *)fromObjects toObjects:(NSArray *)toObjects {
return ^IGListTransitionData *{
return [[IGListTransitionData alloc] initFromObjects:fromObjects toObjects:toObjects toSectionControllers:@[]];
};
}
- (void)setUp {
[super setUp];
@ -50,8 +57,8 @@
self.dataSource = [[IGListTestUICollectionViewDataSource alloc] initWithCollectionView:self.collectionView];
self.updater = [IGListExperimentalAdapterUpdater new];
__weak __typeof__(self) weakSelf = self;
self.updateBlock = ^(NSArray *obj) {
weakSelf.dataSource.sections = obj;
self.applyDataBlock = ^(IGListTransitionData *data) {
weakSelf.dataSource.sections = data.toObjects;
};
}
@ -63,28 +70,39 @@
self.updater = nil;
}
- (void)test_whenUpdatingWithNil_thatUpdaterHasNoChanges {
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:nil toObjectsBlock:nil animated:YES objectTransitionBlock:self.updateBlock completion:nil];
XCTAssertFalse([self.updater hasChanges]);
}
- (void)test_whenUpdatingtoObjects_thatUpdaterHasChanges {
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:nil toObjectsBlock:^NSArray *{return @[@0];} animated:YES objectTransitionBlock:self.updateBlock completion:nil];
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:@[] toObjects:@[@0]]
applyDataBlock:self.applyDataBlock
completion:nil];
XCTAssertTrue([self.updater hasChanges]);
}
- (void)test_whenUpdatingfromObjects_thatUpdaterHasChanges {
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:@[@0] toObjectsBlock:^NSArray *{return nil;} animated:YES objectTransitionBlock:self.updateBlock completion:nil];
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:@[@0] toObjects:@[]]
applyDataBlock:self.applyDataBlock
completion:nil];
XCTAssertTrue([self.updater hasChanges]);
}
- (void)test_whenUpdatingtoObjects_withfromObjects_thatUpdaterHasChanges {
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:@[@0] toObjectsBlock:^NSArray *{return @[@1];} animated:YES objectTransitionBlock:self.updateBlock completion:nil];
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:@[@0] toObjects:@[@1]]
applyDataBlock:self.applyDataBlock
completion:nil];
XCTAssertTrue([self.updater hasChanges]);
}
- (void)test_whenCleaningUpState_withChanges_thatUpdaterHasNoChanges {
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:nil toObjectsBlock:^NSArray *{return @[@0];} animated:YES objectTransitionBlock:self.updateBlock completion:nil];
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:@[] toObjects:@[@0]]
applyDataBlock:self.applyDataBlock
completion:nil];
XCTAssertTrue([self.updater hasChanges]);
[self.updater cleanStateBeforeUpdates];
XCTAssertFalse([self.updater hasChanges]);
@ -112,21 +130,23 @@
- (void)test_whenInsertingSection_thatCollectionViewUpdates {
NSArray *from = @[
[IGSectionObject sectionWithObjects:@[]]
];
IGListToObjectBlock to = ^NSArray *{
return @[
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]]
];
};
[IGSectionObject sectionWithObjects:@[]]
];
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]]
];
self.dataSource.sections = from;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
XCTAssertEqual([self.collectionView numberOfSections], 1);
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:to animated:YES objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
XCTAssertEqual([self.collectionView numberOfSections], 2);
[expectation fulfill];
}];
@ -135,21 +155,23 @@
- (void)test_whenDeletingSection_thatCollectionViewUpdates {
NSArray *from = @[
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]]
];
IGListToObjectBlock to = ^NSArray *{
return @[
[IGSectionObject sectionWithObjects:@[]]
];
};
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]]
];
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[]]
];
self.dataSource.sections = from;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
XCTAssertEqual([self.collectionView numberOfSections], 2);
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:to animated:YES objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
XCTAssertEqual([self.collectionView numberOfSections], 1);
[expectation fulfill];
}];
@ -158,14 +180,12 @@
- (void)test_whenInsertingSection_withItemChanges_thatCollectionViewUpdates {
NSArray *from = @[
[IGSectionObject sectionWithObjects:@[@0]]
];
IGListToObjectBlock to = ^NSArray *{
return @[
[IGSectionObject sectionWithObjects:@[@0, @1]],
[IGSectionObject sectionWithObjects:@[@0, @1]]
];
};
[IGSectionObject sectionWithObjects:@[@0]]
];
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[@0, @1]],
[IGSectionObject sectionWithObjects:@[@0, @1]]
];
self.dataSource.sections = from;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
@ -173,7 +193,11 @@
XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 1);
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:to animated:YES objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
XCTAssertEqual([self.collectionView numberOfSections], 2);
XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 2);
XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 2);
@ -184,16 +208,14 @@
- (void)test_whenInsertingSection_withDeletedSection_thatCollectionViewUpdates {
NSArray *from = @[
[IGSectionObject sectionWithObjects:@[@0, @1, @2]],
[IGSectionObject sectionWithObjects:@[]]
];
IGListToObjectBlock to = ^NSArray *{
return @[
[IGSectionObject sectionWithObjects:@[@1, @1]],
[IGSectionObject sectionWithObjects:@[@0]],
[IGSectionObject sectionWithObjects:@[@0, @2, @3]]
];
};
[IGSectionObject sectionWithObjects:@[@0, @1, @2]],
[IGSectionObject sectionWithObjects:@[]]
];
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[@1, @1]],
[IGSectionObject sectionWithObjects:@[@0]],
[IGSectionObject sectionWithObjects:@[@0, @2, @3]]
];
self.dataSource.sections = from;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
@ -202,7 +224,11 @@
XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 3);
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:to animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
XCTAssertEqual([self.collectionView numberOfSections], 3);
XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 2);
XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 1);
@ -235,14 +261,12 @@
- (void)test_whenCollectionViewNeedsLayout_thatPerformBatchUpdateWorks {
NSArray *from = @[
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]]
];
IGListToObjectBlock to = ^NSArray *{
return @[
[IGSectionObject sectionWithObjects:@[]]
];
};
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]]
];
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[]]
];
self.dataSource.sections = from;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
@ -253,7 +277,11 @@
[self.collectionView setNeedsLayout];
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:to animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
XCTAssertEqual([self.collectionView numberOfSections], 1);
[expectation fulfill];
}];
@ -262,14 +290,12 @@
- (void)test_whenUpdatesAreReentrant_thatUpdatesExecuteSerially {
NSArray *from = @[
[IGSectionObject sectionWithObjects:@[]],
];
IGListToObjectBlock to = ^NSArray *{
return @[
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]],
];
};
[IGSectionObject sectionWithObjects:@[]],
];
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]],
];
self.dataSource.sections = from;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
@ -278,14 +304,16 @@
XCTestExpectation *expectation1 = genExpectation;
void (^preUpdateBlock)(void) = ^{
NSArray *(^anotherTo)(void) = ^NSArray *{
return @[
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]]
];
};
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:anotherTo animated:YES objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
NSArray *anotherTo = @[
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]]
];
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:to toObjects:anotherTo]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
completionCounter++;
XCTAssertEqual([self.collectionView numberOfSections], 3);
XCTAssertEqual(completionCounter, 2);
@ -294,14 +322,18 @@
};
XCTestExpectation *expectation2 = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:to animated:YES objectTransitionBlock:^(NSArray *toObjects) {
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:^(IGListTransitionData *data) {
// executing this block within the updater is just before performBatchUpdates: are applied
// should be able to queue another update here, similar to an update being queued between it beginning and executing
// the performBatchUpdates: block
preUpdateBlock();
self.dataSource.sections = toObjects;
} completion:^(BOOL finished) {
self.dataSource.sections = data.toObjects;
}
completion:^(BOOL finished) {
completionCounter++;
XCTAssertEqual([self.collectionView numberOfSections], 2);
XCTAssertEqual(completionCounter, 1);
@ -328,14 +360,14 @@
__block BOOL itemUpdateBlockExecuted = NO;
__block BOOL sectionUpdateBlockExecuted = NO;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock]
fromObjects:nil
toObjectsBlock:^NSArray *{return @[[IGSectionObject sectionWithObjects:@[@1]]];}
animated:YES objectTransitionBlock:^(NSArray * toObjects) {
self.dataSource.sections = toObjects;
sectionUpdateBlockExecuted = YES;
}
completion:nil];
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:@[] toObjects:@[[IGSectionObject sectionWithObjects:@[@1]]]]
applyDataBlock:^(IGListTransitionData * data) {
self.dataSource.sections = data.toObjects;
sectionUpdateBlockExecuted = YES;
}
completion:nil];
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] animated:YES itemUpdates:^{
@ -353,10 +385,10 @@
- (void)test_whenItemsMoveAndUpdate_thatCollectionViewWorks {
NSArray<IGSectionObject *> *from = @[
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]],
];
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]],
[IGSectionObject sectionWithObjects:@[]],
];
// change the number of items in the section, which a move would be unable to handle and would throw
// keep the same pointers so that the objects are equal
@ -364,14 +396,11 @@
[from[0] setObjects:@[@1, @1]];
[from[1] setObjects:@[@1, @1, @1]];
IGListToObjectBlock to = ^NSArray *{
// rearrange the modified objects
return @[
from[2],
from[0],
from[1]
];
};
NSArray *to = @[
from[2],
from[0],
from[1]
];
self.dataSource.sections = from;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
@ -380,7 +409,11 @@
self.updater.sectionMovesAsDeletesInserts = YES;
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:to animated:YES objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
XCTAssertEqual([self.collectionView numberOfSections], 3);
XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 1);
XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 2);
@ -508,7 +541,11 @@
[[mockDelegate expect] listAdapterUpdater:self.updater didPerformBatchUpdates:OCMOCK_ANY collectionView:self.collectionView];
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:self.dataSource.sections toObjectsBlock:genToBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:self.dataSource.sections toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -526,12 +563,14 @@
[[mockDelegate expect] listAdapterUpdater:self.updater didReloadDataWithCollectionView:self.collectionView isFallbackReload:YES];
XCTestExpectation *expectation = genExpectation;
IGListToObjectBlock to = ^NSArray *{
return @[
[IGSectionObject sectionWithObjects:@[]]
];
};
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:self.dataSource.sections toObjectsBlock:to animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[]]
];
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:self.dataSource.sections toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -549,13 +588,15 @@
[[mockDelegate expect] listAdapterUpdater:self.updater didReloadDataWithCollectionView:self.collectionView isFallbackReload:YES];
XCTestExpectation *expectation = genExpectation;
IGListToObjectBlock to = ^NSArray *{
return @[
[IGSectionObject sectionWithObjects:@[]]
];
};
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[]]
];
self.collectionView.dataSource = nil;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:self.dataSource.sections toObjectsBlock:to animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:self.dataSource.sections toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -572,7 +613,8 @@
}];
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] animated:YES itemUpdates:^{
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock]
animated:YES itemUpdates:^{
object.objects = @[@2, @1, @4, @5];
[self.updater insertItemsIntoCollectionView:self.collectionView indexPaths:@[
[NSIndexPath indexPathForItem:2 inSection:0],
@ -600,16 +642,15 @@
__block BOOL objectTransitionBlockExecuted = NO;
__block BOOL completionBlockExecuted = NO;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock]
fromObjects:self.dataSource.sections
toObjectsBlock:^NSArray *{return self.dataSource.sections;}
animated:YES
objectTransitionBlock:^(NSArray *toObjects) {
objectTransitionBlockExecuted = YES;
}
completion:^(BOOL finished) {
completionBlockExecuted = YES;
}];
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:self.dataSource.sections toObjects:self.dataSource.sections]
applyDataBlock:^(IGListTransitionData *data) {
objectTransitionBlockExecuted = YES;
}
completion:^(BOOL finished) {
completionBlockExecuted = YES;
}];
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] animated:YES itemUpdates:^{
@ -712,7 +753,11 @@
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:genToBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -734,8 +779,12 @@
// Manually set the data source to be nil.
self->_collectionView.dataSource = nil;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:genToBlock animated:NO objectTransitionBlock:^(NSArray * _Nonnull toObjects) {
} completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:^(IGListTransitionData *data) {
}
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -785,23 +834,25 @@
[IGSectionObject sectionWithObjects:@[@0, @1]],
[IGSectionObject sectionWithObjects:@[@0, @1]]
];
IGListToObjectBlock toObjectsBlock2 = ^NSArray *{
return objects2;
};
IGListToObjectBlock toObjectsBlock3 = ^NSArray *{
return objects3;
};
self.dataSource.sections = objects1;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:objects1 toObjectsBlock:toObjectsBlock2 animated:YES objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:objects1 toObjects:objects2]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
XCTAssertEqual([self.collectionView numberOfSections], 2);
XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 2);
XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 2);
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:objects2 toObjectsBlock:toObjectsBlock3 animated:YES objectTransitionBlock:self.updateBlock completion:^(BOOL finished2) {
[self.updater performExperimentalUpdateAnimated:YES
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:objects2 toObjects:objects3]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished2) {
XCTAssertEqual([self.collectionView numberOfSections], 3);
XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 2);
XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 2);
@ -822,9 +873,6 @@
[IGSectionObject sectionWithObjects:@[] identifier:@"0"],
[IGSectionObject sectionWithObjects:@[] identifier:@"1"]
];
IGListToObjectBlock toBlock = ^NSArray *{
return to;
};
self.dataSource.sections = from;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
@ -842,7 +890,11 @@
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:toBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -876,7 +928,11 @@
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:genToBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -908,7 +964,11 @@
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:genToBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -946,7 +1006,11 @@
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:genToBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -984,7 +1048,11 @@
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:genToBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -1024,7 +1092,11 @@
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:genToBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
@ -1059,7 +1131,11 @@
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:genToBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[self.updater performExperimentalUpdateAnimated:NO
collectionViewBlock:[self collectionViewBlock]
dataBlock:[self dataBlockFromObjects:from toObjects:to]
applyDataBlock:self.applyDataBlock
completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;