2019-12-19 17:32:49 +00:00
|
|
|
/*
|
2023-04-06 09:44:16 +00:00
|
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
2016-09-07 22:37:59 +00:00
|
|
|
*
|
2018-05-01 21:33:50 +00:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2016-09-07 22:37:59 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#import "IGListBatchUpdateData.h"
|
2016-12-14 07:01:21 +00:00
|
|
|
|
2016-09-07 22:37:59 +00:00
|
|
|
#import <unordered_map>
|
|
|
|
|
|
2021-09-01 02:22:18 +00:00
|
|
|
#if !__has_include(<IGListDiffKit/IGListDiffKit.h>)
|
|
|
|
|
#import "IGListAssert.h"
|
|
|
|
|
#else
|
2019-10-31 15:17:38 +00:00
|
|
|
#import <IGListDiffKit/IGListAssert.h>
|
2021-09-01 02:22:18 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#import "IGListCompatibility.h"
|
2016-09-07 22:37:59 +00:00
|
|
|
|
|
|
|
|
// Plucks the given move from available moves and turns it into a delete + insert
|
|
|
|
|
static void convertMoveToDeleteAndInsert(NSMutableSet<IGListMoveIndex *> *moves,
|
|
|
|
|
IGListMoveIndex *move,
|
|
|
|
|
NSMutableIndexSet *deletes,
|
|
|
|
|
NSMutableIndexSet *inserts) {
|
|
|
|
|
[moves removeObject:move];
|
|
|
|
|
|
|
|
|
|
// add a delete and insert respecting the move's from and to sections
|
|
|
|
|
// delete + insert will result in reloading the entire section
|
|
|
|
|
[deletes addIndex:move.from];
|
|
|
|
|
[inserts addIndex:move.to];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@implementation IGListBatchUpdateData
|
|
|
|
|
|
|
|
|
|
// Converts all section moves that have index path operations into a section delete + insert.
|
2018-04-02 22:06:52 +00:00
|
|
|
+ (void)_cleanIndexPathsWithMap:(const std::unordered_map<NSInteger, IGListMoveIndex*> &)map
|
2016-09-07 22:37:59 +00:00
|
|
|
moves:(NSMutableSet<IGListMoveIndex *> *)moves
|
2017-04-20 00:01:12 +00:00
|
|
|
indexPaths:(NSMutableArray<NSIndexPath *> *)indexPaths
|
2016-09-07 22:37:59 +00:00
|
|
|
deletes:(NSMutableIndexSet *)deletes
|
|
|
|
|
inserts:(NSMutableIndexSet *)inserts {
|
2023-05-02 09:23:55 +00:00
|
|
|
if (indexPaths.count == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-04-20 00:01:12 +00:00
|
|
|
for (NSInteger i = indexPaths.count - 1; i >= 0; i--) {
|
|
|
|
|
NSIndexPath *path = indexPaths[i];
|
2016-09-07 22:37:59 +00:00
|
|
|
const auto it = map.find(path.section);
|
|
|
|
|
if (it != map.end() && it->second != nil) {
|
2017-04-20 00:01:12 +00:00
|
|
|
[indexPaths removeObjectAtIndex:i];
|
2016-09-07 22:37:59 +00:00
|
|
|
convertMoveToDeleteAndInsert(moves, it->second, deletes, inserts);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Converts all section moves that are also reloaded, or have index path inserts, deletes, or reloads into a section
|
|
|
|
|
delete + insert in order to avoid UICollectionView heap corruptions, exceptions, and animation/snapshot bugs.
|
|
|
|
|
*/
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
- (instancetype)initWithInsertSections:(nonnull NSIndexSet *)insertSections
|
|
|
|
|
deleteSections:(nonnull NSIndexSet *)deleteSections
|
|
|
|
|
moveSections:(nonnull NSSet<IGListMoveIndex *> *)moveSections
|
|
|
|
|
insertIndexPaths:(nonnull NSArray<NSIndexPath *> *)insertIndexPaths
|
|
|
|
|
deleteIndexPaths:(nonnull NSArray<NSIndexPath *> *)deleteIndexPaths
|
|
|
|
|
updateIndexPaths:(nonnull NSArray<NSIndexPath *> *)updateIndexPaths
|
2024-12-12 12:02:03 +00:00
|
|
|
moveIndexPaths:(nonnull NSArray<IGListMoveIndexPath *> *)moveIndexPaths {
|
2016-09-07 22:37:59 +00:00
|
|
|
IGParameterAssert(insertSections != nil);
|
|
|
|
|
IGParameterAssert(deleteSections != nil);
|
|
|
|
|
IGParameterAssert(moveSections != nil);
|
|
|
|
|
IGParameterAssert(insertIndexPaths != nil);
|
|
|
|
|
IGParameterAssert(deleteIndexPaths != nil);
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
IGParameterAssert(updateIndexPaths != nil);
|
2017-02-11 01:52:35 +00:00
|
|
|
IGParameterAssert(moveIndexPaths != nil);
|
2016-09-07 22:37:59 +00:00
|
|
|
if (self = [super init]) {
|
|
|
|
|
NSMutableSet<IGListMoveIndex *> *mMoveSections = [moveSections mutableCopy];
|
|
|
|
|
NSMutableIndexSet *mDeleteSections = [deleteSections mutableCopy];
|
|
|
|
|
NSMutableIndexSet *mInsertSections = [insertSections mutableCopy];
|
2017-02-11 01:52:35 +00:00
|
|
|
NSMutableSet<IGListMoveIndexPath *> *mMoveIndexPaths = [moveIndexPaths mutableCopy];
|
2016-09-07 22:37:59 +00:00
|
|
|
|
|
|
|
|
// these collections should NEVER be mutated during cleanup passes, otherwise sections that have multiple item
|
|
|
|
|
// changes (e.g. a moved section that has a delete + reload on different index paths w/in the section) will only
|
|
|
|
|
// convert one of the item changes into a section delete+insert. this will fail hard and be VERY difficult to
|
|
|
|
|
// debug
|
2017-01-22 22:34:32 +00:00
|
|
|
const NSInteger moveCount = [moveSections count];
|
2023-05-02 09:23:55 +00:00
|
|
|
std::unordered_map<NSInteger, IGListMoveIndex*> fromMap(MAX(moveCount, 1));
|
|
|
|
|
std::unordered_map<NSInteger, IGListMoveIndex*> toMap(MAX(moveCount, 1));
|
2016-09-07 22:37:59 +00:00
|
|
|
for (IGListMoveIndex *move in moveSections) {
|
2017-01-22 22:34:32 +00:00
|
|
|
const NSInteger from = move.from;
|
|
|
|
|
const NSInteger to = move.to;
|
2016-09-07 22:37:59 +00:00
|
|
|
|
2017-03-24 05:26:44 +00:00
|
|
|
// if the move is already deleted or inserted, discard it because count-changing operations must match
|
|
|
|
|
// with data source changes
|
2016-09-07 22:37:59 +00:00
|
|
|
if ([deleteSections containsIndex:from] || [insertSections containsIndex:to]) {
|
2017-03-24 05:26:44 +00:00
|
|
|
[mMoveSections removeObject:move];
|
2016-09-07 22:37:59 +00:00
|
|
|
} else {
|
|
|
|
|
fromMap[from] = move;
|
|
|
|
|
toMap[to] = move;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-12 12:02:03 +00:00
|
|
|
|
fix crash when reloading the same cell index multiple times
Summary:
## Context
Merging multiple batch updates of the same section is inherently unsafe, because each block can mutate the underlaying list and lead to insert/deletes that operate on different before/after lists. The long term fix is to get rid of item level batch updates (insert/delete calls) and use diffing like for sections, but that's a much larger project.
Right now, we have a crash spiking because we're reloading the same cell multiple times. Each time `IGListDiff(...)` returns a pair of insert/delete for the same index (lets say 0). Once these updates are merge together, they look like this:
```
Item inserts = 0, 0
Item deletes = 0, 0
```
We dedupe deletes in `IGListBatchUpdateData`, but don't do anything for inserts, so it looks liket this:
```
Item inserts = 0, 0
Item deletes = 0
```
So we create an insert/delete unbalance and can crash. Tbh, not sure how this hasn't been a bigger issue in the past.
D21822502 tried to remove all duplicate inserts, but that caused other types of crashes, because multiple inserts are allowed.
## This diff
* Lets keep the net insert/delete count the same by keeping track of the number of deletes we remove and apply the same to inserts.
* Add some tests
* Add an experiment flag to kill this if needed to be safe. IG only: Note that I'm making the default true because the crash is happening pretty at start-up.
## Follow up
Over time, the batch update clean up logic was slowly added to multiple place (IGListBatchUpdateData, IGListAdapterUpdaterHelpers, IGListIndexPathResult), which is making it hard to follow. A first step would be to move it in 1 or 2 places and avoid redundant logic (like getting unique deletes). However, the real fix is getting rid of batch updates all together.
Differential Revision: D61719884
fbshipit-source-id: eda7c264c8239a6a106dbe0256fe777b38fae335
2024-08-23 18:32:56 +00:00
|
|
|
NSMutableArray<NSIndexPath *> *mDeleteIndexPaths;
|
|
|
|
|
NSMutableArray<NSIndexPath *> *mInsertIndexPaths;
|
2016-09-07 22:37:59 +00:00
|
|
|
|
fix crash when reloading the same cell index multiple times
Summary:
## Context
Merging multiple batch updates of the same section is inherently unsafe, because each block can mutate the underlaying list and lead to insert/deletes that operate on different before/after lists. The long term fix is to get rid of item level batch updates (insert/delete calls) and use diffing like for sections, but that's a much larger project.
Right now, we have a crash spiking because we're reloading the same cell multiple times. Each time `IGListDiff(...)` returns a pair of insert/delete for the same index (lets say 0). Once these updates are merge together, they look like this:
```
Item inserts = 0, 0
Item deletes = 0, 0
```
We dedupe deletes in `IGListBatchUpdateData`, but don't do anything for inserts, so it looks liket this:
```
Item inserts = 0, 0
Item deletes = 0
```
So we create an insert/delete unbalance and can crash. Tbh, not sure how this hasn't been a bigger issue in the past.
D21822502 tried to remove all duplicate inserts, but that caused other types of crashes, because multiple inserts are allowed.
## This diff
* Lets keep the net insert/delete count the same by keeping track of the number of deletes we remove and apply the same to inserts.
* Add some tests
* Add an experiment flag to kill this if needed to be safe. IG only: Note that I'm making the default true because the crash is happening pretty at start-up.
## Follow up
Over time, the batch update clean up logic was slowly added to multiple place (IGListBatchUpdateData, IGListAdapterUpdaterHelpers, IGListIndexPathResult), which is making it hard to follow. A first step would be to move it in 1 or 2 places and avoid redundant logic (like getting unique deletes). However, the real fix is getting rid of batch updates all together.
Differential Revision: D61719884
fbshipit-source-id: eda7c264c8239a6a106dbe0256fe777b38fae335
2024-08-23 18:32:56 +00:00
|
|
|
// Avoid a flaky UICollectionView bug when deleting from the same index path twice
|
2017-04-20 00:01:12 +00:00
|
|
|
// exposes a possible data source inconsistency issue
|
2024-12-12 12:02:03 +00:00
|
|
|
NSMutableDictionary<NSIndexPath *, NSNumber *> *const deleteCounts = [NSMutableDictionary new];
|
fix crash when reloading the same cell index multiple times
Summary:
## Context
Merging multiple batch updates of the same section is inherently unsafe, because each block can mutate the underlaying list and lead to insert/deletes that operate on different before/after lists. The long term fix is to get rid of item level batch updates (insert/delete calls) and use diffing like for sections, but that's a much larger project.
Right now, we have a crash spiking because we're reloading the same cell multiple times. Each time `IGListDiff(...)` returns a pair of insert/delete for the same index (lets say 0). Once these updates are merge together, they look like this:
```
Item inserts = 0, 0
Item deletes = 0, 0
```
We dedupe deletes in `IGListBatchUpdateData`, but don't do anything for inserts, so it looks liket this:
```
Item inserts = 0, 0
Item deletes = 0
```
So we create an insert/delete unbalance and can crash. Tbh, not sure how this hasn't been a bigger issue in the past.
D21822502 tried to remove all duplicate inserts, but that caused other types of crashes, because multiple inserts are allowed.
## This diff
* Lets keep the net insert/delete count the same by keeping track of the number of deletes we remove and apply the same to inserts.
* Add some tests
* Add an experiment flag to kill this if needed to be safe. IG only: Note that I'm making the default true because the crash is happening pretty at start-up.
## Follow up
Over time, the batch update clean up logic was slowly added to multiple place (IGListBatchUpdateData, IGListAdapterUpdaterHelpers, IGListIndexPathResult), which is making it hard to follow. A first step would be to move it in 1 or 2 places and avoid redundant logic (like getting unique deletes). However, the real fix is getting rid of batch updates all together.
Differential Revision: D61719884
fbshipit-source-id: eda7c264c8239a6a106dbe0256fe777b38fae335
2024-08-23 18:32:56 +00:00
|
|
|
|
2024-12-12 12:02:03 +00:00
|
|
|
// If we need to remove a duplicate delete, we also need to remove an insert to balance the count.
|
|
|
|
|
// Lets build the delete counts for each index, which we can use to skip corresponding inserts.
|
|
|
|
|
for (NSIndexPath *deleteIndexPath in deleteIndexPaths) {
|
|
|
|
|
const NSInteger deleteCount = deleteCounts[deleteIndexPath].integerValue;
|
|
|
|
|
deleteCounts[deleteIndexPath] = @(deleteCount + 1);
|
|
|
|
|
}
|
fix crash when reloading the same cell index multiple times
Summary:
## Context
Merging multiple batch updates of the same section is inherently unsafe, because each block can mutate the underlaying list and lead to insert/deletes that operate on different before/after lists. The long term fix is to get rid of item level batch updates (insert/delete calls) and use diffing like for sections, but that's a much larger project.
Right now, we have a crash spiking because we're reloading the same cell multiple times. Each time `IGListDiff(...)` returns a pair of insert/delete for the same index (lets say 0). Once these updates are merge together, they look like this:
```
Item inserts = 0, 0
Item deletes = 0, 0
```
We dedupe deletes in `IGListBatchUpdateData`, but don't do anything for inserts, so it looks liket this:
```
Item inserts = 0, 0
Item deletes = 0
```
So we create an insert/delete unbalance and can crash. Tbh, not sure how this hasn't been a bigger issue in the past.
D21822502 tried to remove all duplicate inserts, but that caused other types of crashes, because multiple inserts are allowed.
## This diff
* Lets keep the net insert/delete count the same by keeping track of the number of deletes we remove and apply the same to inserts.
* Add some tests
* Add an experiment flag to kill this if needed to be safe. IG only: Note that I'm making the default true because the crash is happening pretty at start-up.
## Follow up
Over time, the batch update clean up logic was slowly added to multiple place (IGListBatchUpdateData, IGListAdapterUpdaterHelpers, IGListIndexPathResult), which is making it hard to follow. A first step would be to move it in 1 or 2 places and avoid redundant logic (like getting unique deletes). However, the real fix is getting rid of batch updates all together.
Differential Revision: D61719884
fbshipit-source-id: eda7c264c8239a6a106dbe0256fe777b38fae335
2024-08-23 18:32:56 +00:00
|
|
|
|
2024-12-12 12:02:03 +00:00
|
|
|
// Skip inserts that have an associated skipped delete
|
|
|
|
|
NSMutableArray<NSIndexPath *> *const trimmedInsertIndexPath = [NSMutableArray new];
|
|
|
|
|
for (NSIndexPath *insertIndexPath in insertIndexPaths) {
|
|
|
|
|
const NSInteger deleteCount = deleteCounts[insertIndexPath].integerValue;
|
|
|
|
|
if (deleteCount > 1) {
|
|
|
|
|
// Skip!
|
|
|
|
|
deleteCounts[insertIndexPath] = @(deleteCount - 1);
|
|
|
|
|
} else {
|
|
|
|
|
[trimmedInsertIndexPath addObject:insertIndexPath];
|
|
|
|
|
}
|
fix crash when reloading the same cell index multiple times
Summary:
## Context
Merging multiple batch updates of the same section is inherently unsafe, because each block can mutate the underlaying list and lead to insert/deletes that operate on different before/after lists. The long term fix is to get rid of item level batch updates (insert/delete calls) and use diffing like for sections, but that's a much larger project.
Right now, we have a crash spiking because we're reloading the same cell multiple times. Each time `IGListDiff(...)` returns a pair of insert/delete for the same index (lets say 0). Once these updates are merge together, they look like this:
```
Item inserts = 0, 0
Item deletes = 0, 0
```
We dedupe deletes in `IGListBatchUpdateData`, but don't do anything for inserts, so it looks liket this:
```
Item inserts = 0, 0
Item deletes = 0
```
So we create an insert/delete unbalance and can crash. Tbh, not sure how this hasn't been a bigger issue in the past.
D21822502 tried to remove all duplicate inserts, but that caused other types of crashes, because multiple inserts are allowed.
## This diff
* Lets keep the net insert/delete count the same by keeping track of the number of deletes we remove and apply the same to inserts.
* Add some tests
* Add an experiment flag to kill this if needed to be safe. IG only: Note that I'm making the default true because the crash is happening pretty at start-up.
## Follow up
Over time, the batch update clean up logic was slowly added to multiple place (IGListBatchUpdateData, IGListAdapterUpdaterHelpers, IGListIndexPathResult), which is making it hard to follow. A first step would be to move it in 1 or 2 places and avoid redundant logic (like getting unique deletes). However, the real fix is getting rid of batch updates all together.
Differential Revision: D61719884
fbshipit-source-id: eda7c264c8239a6a106dbe0256fe777b38fae335
2024-08-23 18:32:56 +00:00
|
|
|
}
|
2019-09-17 22:26:33 +00:00
|
|
|
|
2024-12-12 12:02:03 +00:00
|
|
|
mDeleteIndexPaths = [[deleteCounts allKeys] mutableCopy];
|
|
|
|
|
mInsertIndexPaths = trimmedInsertIndexPath;
|
|
|
|
|
|
|
|
|
|
|
2016-09-07 22:37:59 +00:00
|
|
|
// avoids a bug where a cell is animated twice and one of the snapshot cells is never removed from the hierarchy
|
2018-04-02 22:06:52 +00:00
|
|
|
[IGListBatchUpdateData _cleanIndexPathsWithMap:fromMap moves:mMoveSections indexPaths:mDeleteIndexPaths deletes:mDeleteSections inserts:mInsertSections];
|
2016-09-07 22:37:59 +00:00
|
|
|
|
|
|
|
|
// prevents a bug where UICollectionView corrupts the heap memory when inserting into a section that is moved
|
2018-04-02 22:06:52 +00:00
|
|
|
[IGListBatchUpdateData _cleanIndexPathsWithMap:toMap moves:mMoveSections indexPaths:mInsertIndexPaths deletes:mDeleteSections inserts:mInsertSections];
|
2016-09-07 22:37:59 +00:00
|
|
|
|
2017-02-11 01:52:35 +00:00
|
|
|
for (IGListMoveIndexPath *move in moveIndexPaths) {
|
|
|
|
|
// if the section w/ an index path move is deleted, just drop the move
|
|
|
|
|
if ([deleteSections containsIndex:move.from.section]) {
|
|
|
|
|
[mMoveIndexPaths removeObject:move];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if a move is inside a section that is moved, convert the section move to a delete+insert
|
|
|
|
|
const auto it = fromMap.find(move.from.section);
|
|
|
|
|
if (it != fromMap.end() && it->second != nil) {
|
|
|
|
|
IGListMoveIndex *sectionMove = it->second;
|
|
|
|
|
[mMoveIndexPaths removeObject:move];
|
|
|
|
|
[mMoveSections removeObject:sectionMove];
|
|
|
|
|
[mDeleteSections addIndex:sectionMove.from];
|
|
|
|
|
[mInsertSections addIndex:sectionMove.to];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-07 22:37:59 +00:00
|
|
|
_deleteSections = [mDeleteSections copy];
|
|
|
|
|
_insertSections = [mInsertSections copy];
|
|
|
|
|
_moveSections = [mMoveSections copy];
|
|
|
|
|
_deleteIndexPaths = [mDeleteIndexPaths copy];
|
|
|
|
|
_insertIndexPaths = [mInsertIndexPaths copy];
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
_updateIndexPaths = [updateIndexPaths copy];
|
2017-02-11 01:52:35 +00:00
|
|
|
_moveIndexPaths = [mMoveIndexPaths copy];
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
- (BOOL)isEqual:(id)object {
|
|
|
|
|
if (object == self) {
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
if ([object isKindOfClass:[IGListBatchUpdateData class]]) {
|
|
|
|
|
return ([self.insertSections isEqual:[object insertSections]]
|
|
|
|
|
&& [self.deleteSections isEqual:[object deleteSections]]
|
|
|
|
|
&& [self.moveSections isEqual:[object moveSections]]
|
|
|
|
|
&& [self.insertIndexPaths isEqual:[object insertIndexPaths]]
|
|
|
|
|
&& [self.deleteIndexPaths isEqual:[object deleteIndexPaths]]
|
|
|
|
|
&& [self.updateIndexPaths isEqual:[object updateIndexPaths]]
|
|
|
|
|
&& [self.moveIndexPaths isEqual:[object moveIndexPaths]]);
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-07 22:37:59 +00:00
|
|
|
- (NSString *)description {
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
return [NSString stringWithFormat:@"<%@ %p; deleteSections: %lu; insertSections: %lu; moveSections: %lu; deleteIndexPaths: %lu; insertIndexPaths: %lu; updateIndexPaths: %lu>",
|
Minimum fixes for Xcode 9.3 build errors
Summary:
Issue fixed: #1141
This is a set of minimum fixes for Xcode 9.3 rather than #1142. Much smaller diffs 😃
The changes here were done by Xcode's FixIt. See also https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265-SW5.
> Platform Dependencies
>
> OS X uses several data types—NSInteger, NSUInteger,CGFloat, and CFIndex—to provide a consistent means of representing values in 32- and 64-bit environments. In a 32-bit environment, NSInteger and NSUInteger are defined as int and unsigned int, respectively. In 64-bit environments, NSInteger and NSUInteger are defined as long and unsigned long, respectively. To avoid the need to use different printf-style type specifiers depending on the platform, you can use the specifiers shown in Table 3. Note that in some cases you may have to cast the value.
- [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.
- [x] I have reviewed the [contributing guide](https://github.com/Instagram/IGListKit/blob/master/.github/CONTRIBUTING.md)
Closes https://github.com/Instagram/IGListKit/pull/1143
Differential Revision: D7551617
Pulled By: rnystrom
fbshipit-source-id: b27ebe2b1a1c93ebfd1218dc38b7621fefee2e6b
2018-04-09 15:06:47 +00:00
|
|
|
NSStringFromClass(self.class), self, (unsigned long)self.deleteSections.count, (unsigned long)self.insertSections.count, (unsigned long)self.moveSections.count,
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
(unsigned long)self.deleteIndexPaths.count, (unsigned long)self.insertIndexPaths.count, (unsigned long)self.updateIndexPaths.count];
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|