Repository: airbnb/lottie-ios
File: Sources/Private/Experimental/Animations/CALayer+addAnimation.swift (Swift)

Code before:
    applyDirectlyToLayerIfPossible: Bool = true)
    -> CAPropertyAnimation?
  {
    guard !keyframes.isEmpty else { return nil }

    // If there is exactly one keyframe value, we can improve performance
    // by applying that value directly to the layer instead of creating
    // a relatively expensive `CAKeyframeAnimation`.
    if
      keyframes.count == 1,
      applyDirectlyToLayerIfPossible
    {
      let keyframeValue = keyframeValueMapping(keyframes[0].value)

      // If the keyframe value is the same as the layer's default value for this property,
      // then we can just ignore this set of keyframes.
      if keyframeValue == property.defaultValue {
        return nil
      }

      // If the property on the CALayer being animated hasn't been modified from the default yet,
      // then we can apply the keyframe value directly to the layer using KVC instead
      // of creating a `CAAnimation`. (For example, this check prevents us from using
      // KVC to change the `anchorPoint`, which conflicts with where we set the `anchorPoint`
      // for our layers to a non-default value in `BaseAnimationLayer` / `fillBoundsOfSuperlayer()`)
      let currentValue = value(forKey: property.caLayerKeypath) as? ValueRepresentation

      if currentValue == property.defaultValue {
        setValue(keyframeValue, forKeyPath: property.caLayerKeypath)
        return nil
      }

      // Otherwise, we still need to create a `CAAnimation`, but we can
      // create a simple `CABasicAnimation` that is still less expensive
      // than computing a `CAKeyframeAnimation`.
      let animation = CABasicAnimation(keyPath: property.caLayerKeypath)
      animation.fromValue = keyframeValue
      animation.toValue = keyframeValue
      return animation
    }

    let animation = CAKeyframeAnimation(keyPath: property.caLayerKeypath)

    // Animations using `isHold` should use `CAAnimationCalculationMode.discrete`
    //
    //  - Since we currently only create a single `CAKeyframeAnimation`,
    //    we can currently only correctly support animations where
    //    `isHold` is either always `true` or always `false`
    //    (this requirement doesn't apply to the first/last keyframes).
    //
    //  - We should be able to support this in the future by creating multiple

Review comment (bug):
dropping a note to remember to check this before merging -- since KVC lookups are somewhat expensive, we may want to check if `property.defaultValue` was defined (e.g. is not `nil`) before doing the KVC lookup. I think that shouldn't affect any runtime behaviors, but would just lets us avoid KVC lookups for conditions that are going to fail anyway.

Code after:
    value keyframeValueMapping: (KeyframeValue) -> ValueRepresentation,
    context: LayerAnimationContext)
    -> CAPropertyAnimation?
  {
    guard !keyframes.isEmpty else { return nil }

    // If there is exactly one keyframe value, we can improve performance
    // by applying that value directly to the layer instead of creating
    // a relatively expensive `CAKeyframeAnimation`.
    if keyframes.count == 1 {
      let keyframeValue = keyframeValueMapping(keyframes[0].value)

      // If the keyframe value is the same as the layer's default value for this property,
      // then we can just ignore this set of keyframes.
      if keyframeValue == property.defaultValue {
        return nil
      }

      // If the property on the CALayer being animated hasn't been modified from the default yet,
      // then we can apply the keyframe value directly to the layer using KVC instead
      // of creating a `CAAnimation`.
      if
        let defaultValue = property.defaultValue,
        defaultValue == value(forKey: property.caLayerKeypath) as? ValueRepresentation
      {
        setValue(keyframeValue, forKeyPath: property.caLayerKeypath)
        return nil
      }

      // Otherwise, we still need to create a `CAAnimation`, but we can
      // create a simple `CABasicAnimation` that is still less expensive
      // than computing a `CAKeyframeAnimation`.
      let animation = CABasicAnimation(keyPath: property.caLayerKeypath)
      animation.fromValue = keyframeValue
      animation.toValue = keyframeValue
      return animation
    }

    return keyframeAnimation(
      for: property,
      keyframes: keyframes,
      value: keyframeValueMapping,
      context: context)
  }

  /// A `CAAnimation` that applies the custom value from the `AnyValueProvider`
  /// registered for this specific property's `AnimationKeypath`,
  /// if one has been registered using `AnimationView.setValueProvider(_:keypath:)`.
  @nonobjc
  private func customizedAnimation<ValueRepresentation>(
    for property: LayerProperty<ValueRepresentation>,