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

Code before:
  ///
  /// A value provider provides a typed value on a frame by frame basis.
  ///
  /// - Parameter valueProvider: The new value provider for the properties.
  /// - Parameter keypath: The keypath used to search for properties.
  ///
  /// Example:
  /// ```
  /// /// A keypath that finds the color value for all `Fill 1` nodes.
  /// let fillKeypath = AnimationKeypath(keypath: "**.Fill 1.Color")
  /// /// A Color Value provider that returns a reddish color.
  /// let redValueProvider = ColorValueProvider(Color(r: 1, g: 0.2, b: 0.3, a: 1))
  /// /// Set the provider on the animationView.
  /// animationView.setValueProvider(redValueProvider, keypath: fillKeypath)
  /// ```
  public func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) {
    guard let animationLayer = rootAnimationLayer else { return }

    valueProviders[keypath] = valueProvider
    animationLayer.setValueProvider(valueProvider, keypath: keypath)
  }

  public func removeValueProvider(for keypath: AnimationKeypath) {
    guard let animationLayer = rootAnimationLayer else { return }

    valueProviders[keypath] = nil
    animationLayer.removeValueProvider(for: keypath)
  }

  /// Reads the value of a property specified by the Keypath.
  /// Returns nil if no property is found.
  ///
  /// - Parameter for: The keypath used to search for the property.
  /// - Parameter atFrame: The Frame Time of the value to query. If nil then the current frame is used.
  public func getValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any? {
    rootAnimationLayer?.getValue(for: keypath, atFrame: atFrame)
  }

  /// Reads the original value of a property specified by the Keypath.
  /// This will ignore any value providers and can be useful when implementing a value providers that makes change to the original value from the animation.
  /// Returns nil if no property is found.
  ///
  /// - Parameter for: The keypath used to search for the property.
  /// - Parameter atFrame: The Frame Time of the value to query. If nil then the current frame is used.
  public func getOriginalValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any? {
    rootAnimationLayer?.getOriginalValue(for: keypath, atFrame: atFrame)
  }

  /// Logs all child keypaths.
  public func logHierarchyKeypaths() {
    rootAnimationLayer?.logHierarchyKeypaths()

Review comment (suggestion):
I guess we should also do something like this here:

```suggestion
    valueProviders.removeAll(where: { $0.key.matches(keypath) })
```

Code after:
  ///
  /// A value provider provides a typed value on a frame by frame basis.
  ///
  /// - Parameter valueProvider: The new value provider for the properties.
  /// - Parameter keypath: The keypath used to search for properties.
  ///
  /// Example:
  /// ```
  /// /// A keypath that finds the color value for all `Fill 1` nodes.
  /// let fillKeypath = AnimationKeypath(keypath: "**.Fill 1.Color")
  /// /// A Color Value provider that returns a reddish color.
  /// let redValueProvider = ColorValueProvider(Color(r: 1, g: 0.2, b: 0.3, a: 1))
  /// /// Set the provider on the animationView.
  /// animationView.setValueProvider(redValueProvider, keypath: fillKeypath)
  /// ```
  public func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) {
    guard let animationLayer = rootAnimationLayer else { return }

    valueProviders[keypath] = valueProvider
    animationLayer.setValueProvider(valueProvider, keypath: keypath)
  }

  public func removeValueProvider(for keypath: AnimationKeypath) {
    guard let animationLayer = rootAnimationLayer else { return }

    valueProviders.forEach {
      guard $0.key.matches(keypath) else { return }
      valueProviders[$0.key] = nil
    }
    animationLayer.removeValueProvider(for: keypath)
  }

  /// Reads the value of a property specified by the Keypath.
  /// Returns nil if no property is found.
  ///
  /// - Parameter for: The keypath used to search for the property.
  /// - Parameter atFrame: The Frame Time of the value to query. If nil then the current frame is used.
  public func getValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any? {
    rootAnimationLayer?.getValue(for: keypath, atFrame: atFrame)
  }

  /// Reads the original value of a property specified by the Keypath.
  /// This will ignore any value providers and can be useful when implementing a value providers that makes change to the original value from the animation.
  /// Returns nil if no property is found.
  ///
  /// - Parameter for: The keypath used to search for the property.
  /// - Parameter atFrame: The Frame Time of the value to query. If nil then the current frame is used.
  public func getOriginalValue(for keypath: AnimationKeypath, atFrame: AnimationFrameTime?) -> Any? {
    rootAnimationLayer?.getOriginalValue(for: keypath, atFrame: atFrame)
  }