void AttributeApplicator::applyAttributes()

in RenderCore/View/ComponentViewManager.mm [246:334]


void AttributeApplicator::applyAttributes(UIView *view, std::shared_ptr<const CKViewComponentAttributeValueMap> attributes) noexcept
{
  CK::Component::ActionDisabler actionDisabler; // We never want implicit animations when applying attributes

  // Avoid the static destructor fiasco, use a pointer:
  static const auto *empty = new CKViewComponentAttributeValueMap();

  CKComponentAttributeSetWrapper *const wrapper = attributeSetWrapperForView(view);

  const bool useNewStyleOptimisticMutations = CKReadGlobalConfig().useNewStyleOptimisticMutations;
  const bool hasNewStyleOptimisticViewMutations = useNewStyleOptimisticMutations && !wrapper->_optimisticViewMutations.empty();

  if (!useNewStyleOptimisticMutations) {
    // Reset optimistic mutations so that applicators see they see the state they expect.
    if (!wrapper->_optimisticViewMutationTeardowns_Old.empty()) {
      const auto copiedTeardowns = wrapper->_optimisticViewMutationTeardowns_Old;
      wrapper->_optimisticViewMutationTeardowns_Old.clear();
      for (CKOptimisticViewMutationTeardown teardown : copiedTeardowns) {
        if (teardown) {
          teardown(view);
        }
      }
    }
  } else {
    for (auto it = wrapper->_optimisticViewMutations.rbegin(); it != wrapper->_optimisticViewMutations.rend(); ++it) {
      if (it->undo) {
        it->undo(view);
      }
    }
  }

  const CKViewComponentAttributeValueMap &oldAttributes = wrapper->_attributes ? *wrapper->_attributes : *empty;
  const CKViewComponentAttributeValueMap &newAttributes = *attributes;

  // First, tear down any attributes that appear in the *old* set but not the new set, and *do* have an unapplicator.
  for (const auto &oldAttr : oldAttributes) {
    if (oldAttr.first.unapplicator) {
      const auto &newAttr = newAttributes.find(oldAttr.first);
      if (newAttr == newAttributes.end()) {
        // There is no new attribute, so we always must call "unapplicator".
        oldAttr.first.unapplicator(view, oldAttr.second);
      } else if (!RCObjectIsEqual(newAttr->second, oldAttr.second)) {
        // If the attribute has an updater, don't call the unapplicator; instead, the updater will be called below.
        if (newAttr->first.updater == nil) {
          oldAttr.first.unapplicator(view, oldAttr.second);
        }
      }
    }
  }

  // Now apply the applicators for all attributes in the *new* set, except those that haven't changed in value.
  for (const auto &newAttr : newAttributes) {
    const auto &oldAttr = oldAttributes.find(newAttr.first);
    if (oldAttr == oldAttributes.end()) {
      // There is no old attribute, so we always must call "applicator".
      newAttr.first.applicator(view, newAttr.second);
    } else if (!RCObjectIsEqual(oldAttr->second, newAttr.second)) {
      // If the attribute has an "updater", call that. Otherwise, call the applicator.
      if (newAttr.first.updater) {
        newAttr.first.updater(view, oldAttr->second, newAttr.second);
      } else {
        newAttr.first.applicator(view, newAttr.second);
      }
    }
  }

  if (hasNewStyleOptimisticViewMutations) {
    auto copiedOptimisticMutations = wrapper->_optimisticViewMutations;

    wrapper->_suppressApply = true;

    for (auto optimisticMutation : copiedOptimisticMutations) {
      optimisticMutation.load(view);
    }

    wrapper->_suppressApply = false;

    if (copiedOptimisticMutations.size() != wrapper->_optimisticViewMutations.size()) {
      copiedOptimisticMutations = wrapper->_optimisticViewMutations;
    }

    for (auto optimisticMutation : copiedOptimisticMutations) {
      optimisticMutation.apply(view);
    }
  }

  // Update the wrapper to reference the new attributes. Don't do this before now since it changes oldAttributes.
  wrapper->_attributes = std::move(attributes);
}