Repository: airbnb/lottie-ios
File: Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift (Swift)

Code before:
          position: keyframe.position.pointValue,
          size: keyframe.size.sizeValue,
          cornerRadius: keyframe.cornerRadius.cgFloatValue,
          direction: rectangle.direction)
          .cgPath()
          .duplicated(times: pathMultiplier)
      },
      context: context)
  }
}

extension Rectangle {
  /// Data that represents how to render a rectangle at a specific point in time
  struct Keyframe {
    let size: Vector3D
    let position: Vector3D
    let cornerRadius: Vector1D
  }

  /// Creates a single array of animatable keyframes from the separate arrays of keyframes in this Rectangle
  func combinedKeyframes(context: LayerAnimationContext) throws-> KeyframeGroup<Rectangle.Keyframe> {
    let combinedKeyframes = Keyframes.combinedIfPossible(size, position, cornerRadius) { untypedValues in
      Keyframe(
        size: untypedValues[0] as! Vector3D,
        position: untypedValues[1] as! Vector3D,
        cornerRadius: untypedValues[2] as! Vector1D)
    }

    if let combinedKeyframes = combinedKeyframes {
      return combinedKeyframes
    } else {
      // If we weren't able to combine all of the keyframes, we have to take the timing values from one property
      // use a static value for the other properties
      return try size.map { sizeValue in
        Keyframe(
          size: sizeValue,
          position: try position.exactlyOneKeyframe(context: context, description: "rectangle position"),
          cornerRadius: try cornerRadius.exactlyOneKeyframe(context: context, description: "rectangle cornerRadius"))
      }
    }
  }
}


Review comment (bug):
If we had variadic generics we could have the input parameters be `(KeyframeGroup<Vector3D>, KeyframeGroup<Vector3D>, KeyframeGroup<Vector1D>)` and have the output parameters be `(Vector3D, Vector3D, Vector1D`). Since we can't represent that in the type system today, we have to just output an `[Any]` and cast the parameters manually.

This implementation is either correct and will always work, or is incorrect and would always crash. Since our test suite tests these methods very extensively, imo it's safe to assume that this will always succeed in production as opposed to doing an `as?` optional unwrap and handing a hypothetical failure case.

Code after:
          position: keyframe.position.pointValue,
          size: keyframe.size.sizeValue,
          cornerRadius: keyframe.cornerRadius.cgFloatValue,
          direction: rectangle.direction)
          .cgPath()
          .duplicated(times: pathMultiplier)
      },
      context: context)
  }
}

extension Rectangle {
  /// Data that represents how to render a rectangle at a specific point in time
  struct Keyframe {
    let size: Vector3D
    let position: Vector3D
    let cornerRadius: Vector1D
  }

  /// Creates a single array of animatable keyframes from the separate arrays of keyframes in this Rectangle
  func combinedKeyframes(context: LayerAnimationContext) throws-> KeyframeGroup<Rectangle.Keyframe> {
    let combinedKeyframes = Keyframes.combinedIfPossible(
      size, position, cornerRadius,
      makeCombinedResult: Rectangle.Keyframe.init)

    if let combinedKeyframes = combinedKeyframes {
      return combinedKeyframes
    } else {
      // If we weren't able to combine all of the keyframes, we have to take the timing values
      // from one property and use a fixed value for the other properties.
      return try size.map { sizeValue in
        Keyframe(
          size: sizeValue,
          position: try position.exactlyOneKeyframe(context: context, description: "rectangle position"),
          cornerRadius: try cornerRadius.exactlyOneKeyframe(context: context, description: "rectangle cornerRadius"))
      }
    }
  }
}