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

Code before:
  // MARK: Lifecycle

  init(
    animation: Animation,
    imageProvider: AnimationImageProvider,
    fontProvider: AnimationFontProvider,
    compatibilityTrackerMode: CompatibilityTracker.Mode)
    throws
  {
    self.animation = animation
    self.imageProvider = imageProvider
    self.fontProvider = fontProvider
    compatibilityTracker = CompatibilityTracker(mode: compatibilityTrackerMode)
    super.init()

    setup()
    try setupChildLayers()
  }

  /// Called by CoreAnimation to create a shadow copy of this layer
  /// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
  override init(layer: Any) {
    guard let typedLayer = layer as? Self else {
      fatalError("init(layer:) incorrectly called with \(type(of: layer))")
    }

    animation = typedLayer.animation
    currentAnimationConfiguration = typedLayer.currentAnimationConfiguration
    imageProvider = typedLayer.imageProvider
    fontProvider = typedLayer.fontProvider
    didSetUpAnimation = typedLayer.didSetUpAnimation
    compatibilityTracker = typedLayer.compatibilityTracker
    super.init(layer: typedLayer)
  }

  required init?(coder _: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  // MARK: Internal

  /// Timing-related configuration to apply to this layer's child `CAAnimation`s
  ///  - This is effectively a configurable subset of `CAMediaTiming`
  struct CAMediaTimingConfiguration: Equatable {
    var autoreverses = false
    var repeatCount: Float = 0
    var speed: Float = 1
    var timeOffset: TimeInterval = 0
  }

  enum PlaybackState: Equatable {

Review comment (suggestion):
I reworked this initializer to `throw` after I encountered a silly issue:

```swift
self.animationLayer = CoreAnimationLayer(
  animation: animation,
  compatibilityTrackerMode: .abort,
  didSetUpAnimation: {
     // ...
     makeAnimationLayer(usingEngine: .mainThread)
     // This could run from inside the initializer, meaning the code that takes the result of the initializer
     // and applies it to `self.animationLayer` would run _after_ we finished setting up the main thread layer,
     // putting the `AnimationView` in an inconsistent state.
  })
```

Code after:
  // MARK: Lifecycle

  /// Initializes a `CALayer` that renders the given animation using `CAAnimation`s.
  ///  - This initializer is throwing, but will only throw when using
  ///    `CompatibilityTracker.Mode.abort`.
  init(
    animation: Animation,
    imageProvider: AnimationImageProvider,
    fontProvider: AnimationFontProvider,
    compatibilityTrackerMode: CompatibilityTracker.Mode)
    throws
  {
    self.animation = animation
    self.imageProvider = imageProvider
    self.fontProvider = fontProvider
    compatibilityTracker = CompatibilityTracker(mode: compatibilityTrackerMode)
    super.init()

    setup()
    try setupChildLayers()
  }

  /// Called by CoreAnimation to create a shadow copy of this layer
  /// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
  override init(layer: Any) {
    guard let typedLayer = layer as? Self else {
      fatalError("init(layer:) incorrectly called with \(type(of: layer))")
    }

    animation = typedLayer.animation
    currentAnimationConfiguration = typedLayer.currentAnimationConfiguration
    imageProvider = typedLayer.imageProvider
    fontProvider = typedLayer.fontProvider
    didSetUpAnimation = typedLayer.didSetUpAnimation
    compatibilityTracker = typedLayer.compatibilityTracker
    super.init(layer: typedLayer)
  }

  required init?(coder _: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  // MARK: Internal

  /// Timing-related configuration to apply to this layer's child `CAAnimation`s
  ///  - This is effectively a configurable subset of `CAMediaTiming`
  struct CAMediaTimingConfiguration: Equatable {
    var autoreverses = false
    var repeatCount: Float = 0
    var speed: Float = 1
    var timeOffset: TimeInterval = 0