in ComponentKit/TransactionalDataSources/Common/Internal/CKDataSourceSplitChangesetModification.mm [461:555]
static CKDataSourceSplitUpdateResult splitUpdatedItems(NSArray<NSArray<CKDataSourceItem *> *> *sections,
NSDictionary<NSIndexPath *, id> *updatedItems,
NSMutableArray<CKComponentController *> *addedComponentControllers,
NSMutableArray<CKComponentController *> *invalidComponentControllers,
const CKSizeRange &sizeRange,
CKDataSourceConfiguration *configuration,
id<NSObject> context,
CKDataSourceChangeset *changeset,
NSDictionary *userInfo,
CKDataSourceState *oldState,
CGSize viewportSize,
CKDataSourceLayoutAxis layoutAxis,
CGPoint contentOffset)
{
if (updatedItems.count == 0) {
return {};
}
NSMutableDictionary<NSIndexPath *, CKDataSourceItem *> *const computedItems = [NSMutableDictionary<NSIndexPath *, CKDataSourceItem *> dictionary];
NSMutableDictionary<NSIndexPath *, id> *mutableUpdatedItems = [updatedItems mutableCopy];
NSMutableDictionary<NSIndexPath *, id> *initialUpdatedItems = [NSMutableDictionary<NSIndexPath *, id> dictionary];
NSMutableDictionary<NSIndexPath *, id> *deferredUpdatedItems = [NSMutableDictionary<NSIndexPath *, id> dictionary];
__block CGSize contentSize = CGSizeZero;
[sections enumerateObjectsUsingBlock:^(NSArray<CKDataSourceItem *> *items, NSUInteger sectionIdx, BOOL *stop) {
[items enumerateObjectsUsingBlock:^(CKDataSourceItem *item, NSUInteger itemIdx, BOOL *stop1) {
NSIndexPath *const indexPath = [NSIndexPath indexPathForItem:itemIdx inSection:sectionIdx];
id const updatedModel = mutableUpdatedItems[indexPath];
[mutableUpdatedItems removeObjectForKey:indexPath];
if (updatedModel == nil) {
contentSize = addSizeToSize(contentSize, [item rootLayout].size());
} else if (contentSizeOverflowsViewport(contentSize, contentOffset, viewportSize, layoutAxis)) {
// If the item was already out of the viewport, we assume that it will still be out
// of the viewport once the item is updated. This assumption may not hold true
// if the update *reduces* the size of the item enough such that it now is inside
// the viewport. In this scenario, we under-render and there is a potential performance
// regression.
deferredUpdatedItems[indexPath] = updatedModel;
contentSize = addSizeToSize(contentSize, [item rootLayout].size());
} else {
// If the item was in the viewport before the update, we assume that the item will still
// be in the viewport after the update. This assumption may not hold true if the update
// *increases* the size of the item such that it is now outside the viewport. In this
// scenario, we over-render, which is not a problem since that is not a regression over
// the original behavior.
CKDataSourceItem *const newItem = CKBuildDataSourceItem([item scopeRoot], {}, sizeRange, configuration, updatedModel, context);
computedItems[indexPath] = newItem;
initialUpdatedItems[indexPath] = updatedModel;
contentSize = addSizeToSize(contentSize, [newItem rootLayout].size());
for (auto componentController : addedControllersFromPreviousScopeRootMatchingPredicate(newItem.scopeRoot,
item.scopeRoot,
&CKComponentControllerInitializeEventPredicate)) {
[addedComponentControllers addObject:componentController];
}
for (auto componentController : removedControllersFromPreviousScopeRootMatchingPredicate(newItem.scopeRoot,
item.scopeRoot,
&CKComponentControllerInvalidateEventPredicate)) {
[invalidComponentControllers addObject:componentController];
}
}
}];
}];
// Anything that is left has an invalid index path.
[mutableUpdatedItems enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath, id model, BOOL *stop) {
if (indexPath.section >= sections.count) {
CKCFatalWithCategory(CKHumanReadableInvalidChangesetOperationType(CKInvalidChangesetOperationTypeUpdate),
@"Invalid section: %lu (>= %lu). Changeset: %@, user info: %@, state: %@",
(unsigned long)indexPath.section,
(unsigned long)sections.count,
changeset,
userInfo,
oldState);
}
NSArray<CKDataSourceItem *> *const section = sections[indexPath.section];
if (indexPath.item >= section.count) {
CKCFatalWithCategory(CKHumanReadableInvalidChangesetOperationType(CKInvalidChangesetOperationTypeUpdate),
@"Invalid item: %lu (>= %lu). Changeset: %@, user info: %@, state: %@",
(unsigned long)indexPath.item,
(unsigned long)section.count,
changeset,
userInfo,
oldState);
}
}];
return {
.splitItems = {
.initialChangesetItems = initialUpdatedItems,
.deferredChangesetItems = deferredUpdatedItems,
},
.computedItems = computedItems,
};
}