Repository: airbnb/lottie-ios
File: Sources/Public/Animation/LottieView.swift (Swift)

Code before:
    } placeholder: {
      placeholder()
    }
  }

  /// Creates a `LottieView` that asynchronously loads and displays the given `LottieAnimationSource`.
  /// The `loadAnimation` closure is called exactly once in `onAppear`.
  /// While the animation is loading, the `placeholder` view is shown in place of the `LottieAnimationView`.
  /// - Parameters:
  ///   - loadAnimationTrigger: `Binding` that  triggers a new call to `loadAnimation` when a new value is propagated
  ///   - loadAnimation: Closure that provides an animation asynchronously
  public init(
    loadAnimationTrigger: Binding<AnyHashable> = .constant(LoadReference.constantSource),
    loadAnimation: @escaping () async throws -> LottieAnimationSource?,
    @ViewBuilder placeholder: @escaping () -> Placeholder)
  {
    self.loadAnimation = loadAnimation
    self.placeholder = placeholder
    self.loadAnimationTrigger = loadAnimationTrigger
    _animationSource = State(initialValue: nil)
  }

  // MARK: Public

  /// Token used when no `loadAnimationTrigger` is provided
  public enum LoadReference {
    case constantSource
  }

  public var body: some View {
    ZStack {
      if let animationSource = animationSource {
        LottieAnimationView.swiftUIView {
          LottieAnimationView(
            animationSource: animationSource,
            imageProvider: imageProvider,
            textProvider: textProvider,
            fontProvider: fontProvider,
            configuration: configuration)
        }
        .sizing(sizing)
        .configure { context in
          // We check referential equality of the animation before updating as updating the
          // animation has a side-effect of rebuilding the animation layer, and it would be
          // prohibitive to do so on every state update.
          if animationSource.animation !== context.view.animation {
            context.view.loadAnimation(animationSource)
          }
        }
        .configurations(configurations)
      } else {

Review comment (suggestion):
Instead of using `loadAnimationTrigger = .constant(LoadReference.constantSource)` to mean "call this only once", could we make it optional and use either `loadAnimationTrigger = nil` or `loadAnimationTrigger = .constant(nil)`? 

It would be nice to not add a new public value just for this functionality

Code after:
            animationSource: animationSource,
            imageProvider: imageProvider,
            textProvider: textProvider,
            fontProvider: fontProvider,
            configuration: configuration)
        }
        .sizing(sizing)
        .configure { context in
          // We check referential equality of the animation before updating as updating the
          // animation has a side-effect of rebuilding the animation layer, and it would be
          // prohibitive to do so on every state update.
          if animationSource.animation !== context.view.animation {
            context.view.loadAnimation(animationSource)
          }
        }
        .configurations(configurations)
      } else {
        placeholder?()
      }
    }
    .onAppear {
      loadAnimationIfNecessary()
    }
    .valueChanged(value: loadAnimationTrigger?.wrappedValue) { _ in
      animationSource = nil
      loadAnimationIfNecessary()
    }
  }

  /// Returns a copy of this `LottieView` updated to have the given closure applied to its
  /// represented `LottieAnimationView` whenever it is updated via the `updateUIView(…)`
  /// or `updateNSView(…)` method.
  public func configure(_ configure: @escaping (LottieAnimationView) -> Void) -> Self {
    var copy = self
    copy.configurations.append { context in
      configure(context.view)
    }
    return copy
  }

  /// Returns a copy of this view that can be resized by scaling its animation to fit the size
  /// offered by its parent.
  public func resizable() -> Self {
    var copy = self
    copy.sizing = .proposed
    return copy
  }

  /// Returns a copy of this view that loops its animation whenever visible by playing
  /// whenever it is updated with a `loopMode` of `.loop` if not already playing.
  public func looping() -> Self {