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

Code before:
            "\(maskLayerModel.name) (mask of \(layerModel.name)) (parent, \(parentLayerModel.name))"
          })

        // Set up a parent container to host both the layer
        // and its mask in the same coordinate space
        let maskContainer = BaseAnimationLayer()
        maskContainer.name = "\(layerModel.name) (parent, masked)"
        maskContainer.addSublayer(parentTransformLayer)
        maskContainer.mask = maskParentTransformLayer
        addSublayer(maskContainer)
      }

      else {
        addSublayer(parentTransformLayer)
      }
    }
  }

}

extension Array where Element == LayerModel {
  /// Pairs each `LayerModel` within this array with
  /// a `LayerModel` to use as its mask, if applicable
  /// based on the layer's `MatteType` configuration.
  ///  - Assumes the layers are sorted in z-axis order.
  var layersAndMasks: [(layer: LayerModel, mask: LayerModel?)] {
    var layersAndMasks = [(layer: LayerModel, mask: LayerModel?)]()
    var unprocessedLayers = self

    while !unprocessedLayers.isEmpty {
      let layer = unprocessedLayers.removeFirst()

      /// If a layer has a `MatteType`, then the next layer will be used as its `mask`
      if
        let matteType = layer.matte,
        matteType != .none,
        !unprocessedLayers.isEmpty
      {
        LottieLogger.shared.assert(
          matteType == .add,
          "The Core Animation rendering engine currently only supports `MatteMode.add`.")

        let maskLayer = unprocessedLayers.removeFirst()
        layersAndMasks.append((layer: layer, mask: maskLayer))
      }

      else {
        layersAndMasks.append((layer: layer, mask: nil))
      }
    }


Review comment (performance):
I believe the Swift API design guidelines recommend against computed properties that are O(N), suggest making this a function like `func pairedLayersAndMasks()`

Code after:
            "\(maskLayerModel.name) (mask of \(layerModel.name)) (parent, \(parentLayerModel.name))"
          })

        // Set up a parent container to host both the layer
        // and its mask in the same coordinate space
        let maskContainer = BaseAnimationLayer()
        maskContainer.name = "\(layerModel.name) (parent, masked)"
        maskContainer.addSublayer(parentTransformLayer)
        maskContainer.mask = maskParentTransformLayer
        addSublayer(maskContainer)
      }

      else {
        addSublayer(parentTransformLayer)
      }
    }
  }

}

extension Collection where Element == LayerModel {
  /// Pairs each `LayerModel` within this array with
  /// a `LayerModel` to use as its mask, if applicable
  /// based on the layer's `MatteType` configuration.
  ///  - Assumes the layers are sorted in z-axis order.
  fileprivate func pairedLayersAndMasks() -> [(layer: LayerModel, mask: LayerModel?)] {
    var layersAndMasks = [(layer: LayerModel, mask: LayerModel?)]()
    var unprocessedLayers = reversed()

    while let layer = unprocessedLayers.popLast() {
      /// If a layer has a `MatteType`, then the next layer will be used as its `mask`
      if
        let matteType = layer.matte,
        matteType != .none,
        let maskLayer = unprocessedLayers.popLast()
      {
        LottieLogger.shared.assert(
          matteType == .add,
          "The Core Animation rendering engine currently only supports `MatteMode.add`.")

        layersAndMasks.append((layer: layer, mask: maskLayer))
      }

      else {
        layersAndMasks.append((layer: layer, mask: nil))
      }
    }

    return layersAndMasks
  }
}