Repository: airbnb/lottie-ios
File: Sources/Public/DotLottie/DotLottieFile.swift (Swift)

Code before:
/// Detailed .lottie file structure
public final class DotLottieFile {

  // MARK: Lifecycle

  /// Loads `DotLottie` from `Data` object containing a compressed animation.
  ///
  /// - Parameters:
  ///  - data: Data of .lottie file
  ///  - filename: Name of .lottie file
  ///  - Returns: Deserialized `DotLottie`. Optional.
  init(data: Data, filename: String) throws {
    fileUrl = DotLottieUtils.tempDirectoryURL.appendingPathComponent(filename.asFilename())
    try decompress(data: data, to: fileUrl)
  }

  // MARK: Public

  /// Definition for a single animation within a `DotLottieFile`
  public struct Animation {
    public let animation: LottieAnimation
    public let configuration: DotLottieConfiguration
  }

  /// The `LottieAnimation` and `DotLottieConfiguration` for the given animation ID in this file
  public func animation(for id: String? = nil) -> DotLottieFile.Animation? {
    if let id = id {
      return animations.first(where: { $0.configuration.id == id })
    } else {
      return animations.first
    }
  }

  /// The `LottieAnimation` and `DotLottieConfiguration` for the given animation index in this file
  public func animation(at index: Int) -> DotLottieFile.Animation? {
    guard index < animations.count else { return nil }
    return animations[index]
  }

  /// Returns the next animation in the array. Returns the first animation in case it's the last index
  /// The next `LottieAnimation` and `DotLottieConfiguration`
  public func nextAnimation(after animationId: String) -> DotLottieFile.Animation? {
    guard let index = animations.firstIndex(where: { animationId == $0.configuration.id }) else {
      return nil
    }

    let nextIndex = index + 1
    if nextIndex < animations.count {
      return animations[nextIndex]
    }


Review comment (refactor):
In the `public` function, could we change `for id: String?` to `for id: String`? 

If we need a helper for handling `nil` IDs, can we keep that private?

Code after:
/// Detailed .lottie file structure
public final class DotLottieFile {

  // MARK: Lifecycle

  /// Loads `DotLottie` from `Data` object containing a compressed animation.
  ///
  /// - Parameters:
  ///  - data: Data of .lottie file
  ///  - filename: Name of .lottie file
  ///  - Returns: Deserialized `DotLottie`. Optional.
  init(data: Data, filename: String) throws {
    fileUrl = DotLottieUtils.tempDirectoryURL.appendingPathComponent(filename.asFilename())
    try decompress(data: data, to: fileUrl)
  }

  // MARK: Public

  /// Definition for a single animation within a `DotLottieFile`
  public struct Animation {
    public let animation: LottieAnimation
    public let configuration: DotLottieConfiguration
  }

  /// List of `LottieAnimation` in the file
  public private(set) var animations: [Animation] = []

  // MARK: Internal

  /// Image provider for animations
  private(set) var imageProvider: AnimationImageProvider?

  /// Animations folder url
  lazy var animationsUrl: URL = fileUrl.appendingPathComponent("\(DotLottieFile.animationsFolderName)")

  /// All files in animations folder
  lazy var animationUrls: [URL] = FileManager.default.urls(for: animationsUrl) ?? []

  /// Images folder url
  lazy var imagesUrl: URL = fileUrl.appendingPathComponent("\(DotLottieFile.imagesFolderName)")

  /// All images in images folder
  lazy var imageUrls: [URL] = FileManager.default.urls(for: imagesUrl) ?? []

  /// The `LottieAnimation` and `DotLottieConfiguration` for the given animation ID in this file
  func animation(for id: String? = nil) -> DotLottieFile.Animation? {
    if let id = id {
      return animations.first(where: { $0.configuration.id == id })
    } else {
      return animations.first
    }