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

Code before:
      coreAnimationLayer.didSetUpAnimation = { [weak self] issues in
        self?.automaticEngineLayerDidSetUpAnimation(issues)
      }

      return coreAnimationLayer
    } catch {
      if case CompatibilityTracker.Error.encounteredCompatibilityIssue(let compatibilityIssue) = error {
        automaticEngineLayerDidSetUpAnimation([compatibilityIssue])
      } else {
        automaticEngineLayerDidSetUpAnimation([])
      }

      return nil
    }
  }

  // Handles any compatibility issues with the Core Animation engine
  // by falling back to the Main Thread engine
  fileprivate func automaticEngineLayerDidSetUpAnimation(_ compatibilityIssues: [CompatibilityIssue]) {
    // If there weren't any compatibility issues, then there's nothing else to do
    if compatibilityIssues.isEmpty {
      return
    }

    LottieLogger.shared.warn(
      compatibilityIssues.compatibilityMessage
        + "\nFalling back to Main Thread rendering engine.\n")

    let animationContext = self.animationContext
    let currentFrame = self.currentFrame

    makeAnimationLayer(usingEngine: .mainThread)

    // Set up the Main Thread animation layer using the same configuration that
    // was being used by the previous Core Animation layer
    self.currentFrame = currentFrame

    if let animationContext = animationContext {
      addNewAnimationForContext(animationContext)
    }
  }

  fileprivate func updateAnimationForBackgroundState() {
    if let currentContext = animationContext {
      switch backgroundBehavior {
      case .stop:
        removeCurrentAnimation()
        updateAnimationFrame(currentContext.playFrom)
      case .pause:
        removeCurrentAnimation()
      case .pauseAndRestore:

Review comment (performance):
Maybe we could include information that this is the first error, but there may be more (since we're using the `abort` mode)? 

And maybe details on the fact that this fallback has performance overhead, and you should specify the main thread rendering engine directly if you're seeing degraded performance

Code after:
  fileprivate func makeAutomaticEngineLayer(for animation: Animation) -> CoreAnimationLayer? {
    do {
      // Attempt to set up the Core Animation layer. This can either throw immediately in `init`,
      // or throw an error later in `CALayer.display()` that will be reported in `didSetUpAnimation`.
      let coreAnimationLayer = try CoreAnimationLayer(
        animation: animation,
        imageProvider: imageProvider,
        fontProvider: fontProvider,
        compatibilityTrackerMode: .abort)

      coreAnimationLayer.didSetUpAnimation = { [weak self] issues in
        self?.automaticEngineLayerDidSetUpAnimation(issues)
      }

      return coreAnimationLayer
    } catch {
      if case CompatibilityTracker.Error.encounteredCompatibilityIssue(let compatibilityIssue) = error {
        automaticEngineLayerDidSetUpAnimation([compatibilityIssue])
      } else {
        // This should never happen, because we expect `CoreAnimationLayer` to only throw
        // `CompatibilityTracker.Error.encounteredCompatibilityIssue` errors.
        LottieLogger.shared.assertionFailure("Encountered unexpected error \(error)")
        automaticEngineLayerDidSetUpAnimation([])
      }

      return nil
    }
  }

  // Handles any compatibility issues with the Core Animation engine
  // by falling back to the Main Thread engine
  fileprivate func automaticEngineLayerDidSetUpAnimation(_ compatibilityIssues: [CompatibilityIssue]) {
    // If there weren't any compatibility issues, then there's nothing else to do
    if compatibilityIssues.isEmpty {
      return
    }

    LottieLogger.shared.warn(
      "Encountered Core Animation compatibility issue while setting up animation:\n"
        + compatibilityIssues.map { $0.description }.joined(separator: "\n") + "\n"
        + """
          This animation may have additional compatibility issues, but animation setup was cancelled early to avoid wasted work.

          Automatically falling back to Main Thread rendering engine. This fallback comes with some additional performance
          overhead, which can be reduced by manually specifying that this animation should always use the Main Thread engine.

          """)

    let animationContext = self.animationContext
    let currentFrame = self.currentFrame