Repository: airbnb/lottie-ios
File: Example/Example/AnimationPreviewView.swift (Swift)

Code before:
      .padding(.all, 16)
      #endif
    }
    .navigationTitle(animationSource.name.components(separatedBy: "/").last!)
    .frame(maxWidth: .infinity, maxHeight: .infinity)
    .background(Color.secondaryBackground)
  }

  // MARK: Private

  @State private var animationPlaying = true
  @State private var sliderValue: AnimationProgressTime = 0

  @ViewBuilder
  private var lottieView: some View {
    switch animationSource {
    case .local(let name):
      localAnimation(animationName: name)
    case .remote(let url):
      LottieView {
        await LottieAnimation.loadedFrom(url: url)
      } placeholder: {
        LoadingIndicator()
      }
      .imageProvider(.exampleAppSampleImages)
      .resizable()
      .looping()
      .currentProgress(animationPlaying ? nil : sliderValue)
      .getRealtimeAnimationProgress(animationPlaying ? $sliderValue : nil)
    }
  }

  @ViewBuilder
  private func localAnimation(animationName: String) -> some View {
    if let animation = LottieAnimation.named(animationName) {
      LottieView(animation: animation)
        .imageProvider(.exampleAppSampleImages)
        .resizable()
        .looping()
        .currentProgress(animationPlaying ? nil : sliderValue)
        .getRealtimeAnimationProgress(animationPlaying ? $sliderValue : nil)
    } else {
      LottieView {
        try await DotLottieFile.named(animationName)
      } placeholder: {
        LoadingIndicator()
      }
      .imageProvider(.exampleAppSampleImages)
      .resizable()
      .looping()
      .currentProgress(animationPlaying ? nil : sliderValue)

Review comment (suggestion):
if we change `private var lottieView: some View` to `private var lottieView: some LottieView` then perhaps we can move the modifiers shared by all of the views into the same place in `body`, like this:

```swift
      lottieView
        .imageProvider(.exampleAppSampleImages)
        .resizable()
        .looping()
        .currentProgress(animationPlaying ? nil : sliderValue)
        .getRealtimeAnimationProgress(animationPlaying ? $sliderValue : nil)
```

It would be nice to not have to update all of those call-sites separately

Code after:
      .getRealtimeAnimationProgress(animationPlaying ? $sliderValue : nil)

      Spacer()

      #if !os(tvOS)
      Slider(value: $sliderValue, in: 0...1, onEditingChanged: { editing in
        if animationPlaying, editing {
          animationPlaying = false
        }
      })
      .padding(.all, 16)
      #endif
    }
    .navigationTitle(animationSource.name.components(separatedBy: "/").last!)
    .frame(maxWidth: .infinity, maxHeight: .infinity)
    .background(Color.secondaryBackground)
  }

  // MARK: Private

  @State private var animationPlaying = true
  @State private var sliderValue: AnimationProgressTime = 0

  private func lottieSource() async throws -> LottieAnimationSource? {
    switch animationSource {
    case .local(let name):
      if let animation = LottieAnimation.named(name) {
        return .lottieAnimation(animation)
      } else {
        let lottie = try await DotLottieFile.named(name)
        return .dotLottieFile(lottie)
      }
    case .remote(let url, _):
      let animation = await LottieAnimation.loadedFrom(url: url)
      return animation.map(LottieAnimationSource.lottieAnimation)
    }
  }
}

extension Color {
  static var secondaryBackground: Color {
    #if os(iOS)
    Color(uiColor: .secondarySystemBackground)
    #else
      .clear
    #endif
  }
}

extension AnimationImageProvider where Self == FilepathImageProvider {
  static var exampleAppSampleImages: FilepathImageProvider {