static CKDataSourceSplitUpdateResult splitUpdatedItems()

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,
  };
}