Repository: airbnb/lottie-ios
File: Sources/Private/Experimental/Layers/BaseCompositionLayer.swift (Swift)

Code before:
  /// 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("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
    }

    baseLayerModel = typedLayer.baseLayerModel
    super.init(layer: typedLayer)
  }

  // MARK: Internal

  /// The layer that the `LayerModel.transform` is applied to
  ///  - Child `LayerModel`s should always be added to this layer,
  ///    so they inherit the transform of the parent layer.
  let transformLayer: CALayer = {
    let layer = CALayer()
    layer.name = "transform"
    return layer
  }()

  /// The layer that should render visible elements displayed by this layer's `LayerModel`
  ///  - This layer applies `LayerModel.transform.opacity`, which applies to this specific
  ///    `LayerModel`'s content and not the content of any child `LayerModel`s.
  let contentsLayer: CALayer = {
    let layer = CALayer()
    layer.name = "contents"
    return layer
  }()

  override func addSublayer(_: CALayer) {
    LottieLogger.shared.assertionFailure("""
    Sublayers should not be added directly to `BaseCompositionLayer`.
    Instead, add sublayers to either `transformLayer` or `contentsLayer`.
    """)
  }

  /// Sets up the base `LayerModel` animations for this layer,
  /// and all child `AnimationLayer`s.
  ///  - Can be overridden by subclasses, which much call `super`.
  func setupAnimations(context: LayerAnimationContext) {
    transformLayer.addTransformAnimations(for: baseLayerModel.transform, context: context)

    // Add the opacity and visibility animations to _only_ the `contentsLayer`
    //  - These animations should affect the content rendered directly by this layer,
    //    but _not_ any child `LayerModel`s.
    contentsLayer.addOpacityAnimation(from: baseLayerModel.transform, context: context)

    contentsLayer.addVisibilityAnimation(
      inFrame: CGFloat(baseLayerModel.inFrame),

Review comment (suggestion):
The distinction between `transformLayer` and `contentsLayer` is no longer necessary, now that we don't add child `LayerModel`s as sublayers

Code after:
  /// 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("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
    }

    baseLayerModel = typedLayer.baseLayerModel
    super.init(layer: typedLayer)
  }

  // MARK: Internal

  /// Whether or not this layer render should render any visible content
  var renderLayerContents: Bool { true }

  /// Sets up the base `LayerModel` animations for this layer,
  /// and all child `AnimationLayer`s.
  ///  - Can be overridden by subclasses, which much call `super`.
  func setupAnimations(context: LayerAnimationContext) {
    addTransformAnimations(for: baseLayerModel.transform, context: context)

    if renderLayerContents {
      addOpacityAnimation(from: baseLayerModel.transform, context: context)

      addVisibilityAnimation(
        inFrame: CGFloat(baseLayerModel.inFrame),
        outFrame: CGFloat(baseLayerModel.outFrame),
        context: context)
    }

    for childAnimationLayer in managedSublayers {
      (childAnimationLayer as? AnimationLayer)?.setupAnimations(context: context)
    }
  }

  override func layoutSublayers() {
    super.layoutSublayers()

    for sublayer in managedSublayers {
      sublayer.fillBoundsOfSuperlayer()
    }
  }

  // MARK: Private

  private let baseLayerModel: LayerModel

  /// All of the sublayers managed by this `BaseCompositionLayer`
  private var managedSublayers: [CALayer] {
    (sublayers ?? []) + [mask].compactMap { $0 }