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

Code before:
    add(timedProgressAnimation, forKey: #keyPath(animationProgress))
  }

  // Removes the current `CAAnimation`s, and rebuilds new animations
  // using the same configuration as the previous animations.
  private func rebuildCurrentAnimation(with newConfiguration: AnimationConfiguration? = nil) {
    guard
      let currentConfiguration = currentAnimationConfiguration,
      let playbackState = playbackState,
      // Don't replace any pending animations that are queued to begin
      // on the next run loop cycle, since an existing pending animation
      // will cause the animation to be rebuilt anyway.
      pendingAnimationConfiguration == nil
    else {
      // If we already have a pending animation setup pass, but a new configuration was provided,
      // replace the pending configuration with the new configuration
      if let newConfiguration = newConfiguration {
        pendingAnimationConfiguration?.animationConfiguration = newConfiguration
      }

      return
    }

    removeAnimations()

    switch playbackState {
    case .paused(let frame):
      currentFrame = frame

    case .playing:
      playAnimation(configuration: newConfiguration ?? currentConfiguration)
    }
  }

}

// MARK: RootAnimationLayer

extension CoreAnimationLayer: RootAnimationLayer {

  var primaryAnimationKey: AnimationKey {
    .specific(#keyPath(animationProgress))
  }

  var isAnimationPlaying: Bool? {
    switch pendingAnimationConfiguration?.playbackState {
    case .playing:
      return true
    case .paused:
      return false
    case nil:

Review comment (suggestion):
Switched from calling `CoreAnimationLayer.playAnimation` to `LottieAnimationView.updateInflightAnimation`, which already handles seamlessly recreating the animation with the `currentFrame`

Code after:
    let animationProgressTracker = CABasicAnimation(keyPath: #keyPath(animationProgress))
    animationProgressTracker.fromValue = 0
    animationProgressTracker.toValue = 1

    let timedProgressAnimation = animationProgressTracker.timed(with: context, for: self)
    timedProgressAnimation.delegate = currentAnimationConfiguration?.animationContext.closure

    // Remove the progress animation once complete so we know when the animation
    // has finished playing (if it doesn't loop infinitely)
    timedProgressAnimation.isRemovedOnCompletion = true

    add(timedProgressAnimation, forKey: #keyPath(animationProgress))
  }

  // Removes the current `CAAnimation`s, and rebuilds new animations
  // using the same configuration as the previous animations.
  private func rebuildCurrentAnimation() {
    guard
      // Don't replace any pending animations that are queued to begin
      // on the next run loop cycle, since an existing pending animation
      // will cause the animation to be rebuilt anyway.
      pendingAnimationConfiguration == nil
    else { return }

    if isAnimationPlaying == true {
      animationView?.updateInFlightAnimation()
    } else {
      let currentFrame = currentFrame
      removeAnimations()
      self.currentFrame = currentFrame
    }
  }

}

// MARK: RootAnimationLayer

extension CoreAnimationLayer: RootAnimationLayer {

  var primaryAnimationKey: AnimationKey {
    .specific(#keyPath(animationProgress))
  }

  /// Whether or not the animation is currently playing.
  ///  - Handles case where CAAnimations with a finite duration animation (e.g. `playOnce`)
  ///    have finished playing but still present on this layer.
  var isAnimationPlaying: Bool? {
    switch pendingAnimationConfiguration?.playbackState {
    case .playing:
      return true
    case .paused: