Repository: expo/expo
File: packages/expo-updates/ios/EXUpdates/AppLoader/AppLoaderTask.swift (Swift)

Code before:
          } else {
            completion()
          }
        }
      }
    }
  }

  private func launch(withCompletion completion: @escaping (_ error: UpdatesError?, _ success: Bool) -> Void) {
    let launcher = AppLauncherWithDatabase(config: config, database: database, directory: directory, completionQueue: loaderTaskQueue, logger: self.logger)
    candidateLauncher = launcher
    launcher.launchUpdate(withSelectionPolicy: selectionPolicy, completion: completion)
  }

  private func loadRemoteUpdate(withCompletion completion: @escaping (_ remoteError: UpdatesError?, _ updateResponse: UpdateResponse?) -> Void) {
    remoteAppLoader = RemoteAppLoader(
      config: config,
      logger: logger,
      database: database,
      directory: directory,
      launchedUpdate: candidateLauncher?.launchedUpdate,
      completionQueue: loaderTaskQueue
    )

    remoteAppLoader?.assetLoadProgressBlock = { progress in
      if let swiftDelegate = self.swiftDelegate {
        self.delegateQueue.async {
          swiftDelegate.appLoaderTask(self, didUpdateProgress: progress)
        }
      }
    }

    if let swiftDelegate = self.swiftDelegate {
      self.delegateQueue.async {
        swiftDelegate.appLoaderTaskDidStartCheckingForRemoteUpdate(self)
      }
    }
    remoteAppLoader!.loadUpdate(
      fromURL: config.updateUrl
    ) { updateResponse in
      if let updateDirective = updateResponse.directiveUpdateResponsePart?.updateDirective {
        switch updateDirective {
        case is NoUpdateAvailableUpdateDirective:
          self.isUpToDate = true
          if let swiftDelegate = self.swiftDelegate {
            self.delegateQueue.async {
              swiftDelegate.appLoaderTask(self, didFinishCheckingForRemoteUpdateWithRemoteCheckResult: RemoteCheckResult.noUpdateAvailable(reason: .noUpdateAvailableOnServer))
            }
          }
          return false
        case let rollBackUpdateDirective as RollBackToEmbeddedUpdateDirective:

Review comment (suggestion):
```suggestion
    remoteAppLoader?.assetLoadProgressBlock = { [weak self] progress in
      guard let self else {
        return
      }
      if let swiftDelegate = self.swiftDelegate {
```

not sure if the retain cycle could potential happen in the assetLoadProgressBlock

Code after:
          } else {
            completion()
          }
        }
      }
    }
  }

  private func launch(withCompletion completion: @escaping (_ error: UpdatesError?, _ success: Bool) -> Void) {
    let launcher = AppLauncherWithDatabase(config: config, database: database, directory: directory, completionQueue: loaderTaskQueue, logger: self.logger)
    candidateLauncher = launcher
    launcher.launchUpdate(withSelectionPolicy: selectionPolicy, completion: completion)
  }

  private func loadRemoteUpdate(withCompletion completion: @escaping (_ remoteError: UpdatesError?, _ updateResponse: UpdateResponse?) -> Void) {
    remoteAppLoader = RemoteAppLoader(
      config: config,
      logger: logger,
      database: database,
      directory: directory,
      launchedUpdate: candidateLauncher?.launchedUpdate,
      completionQueue: loaderTaskQueue
    )

    remoteAppLoader?.assetLoadProgressBlock = { [weak self] progress in
      guard let self else {
        return
      }
      if let swiftDelegate = self.swiftDelegate {
        self.delegateQueue.async {
          swiftDelegate.appLoaderTask(self, didUpdateProgress: progress)
        }
      }
    }

    if let swiftDelegate = self.swiftDelegate {
      self.delegateQueue.async {
        swiftDelegate.appLoaderTaskDidStartCheckingForRemoteUpdate(self)
      }
    }
    remoteAppLoader!.loadUpdate(
      fromURL: config.updateUrl
    ) { updateResponse in
      if let updateDirective = updateResponse.directiveUpdateResponsePart?.updateDirective {
        switch updateDirective {
        case is NoUpdateAvailableUpdateDirective:
          self.isUpToDate = true
          if let swiftDelegate = self.swiftDelegate {
            self.delegateQueue.async {
              swiftDelegate.appLoaderTask(self, didFinishCheckingForRemoteUpdateWithRemoteCheckResult: RemoteCheckResult.noUpdateAvailable(reason: .noUpdateAvailableOnServer))
            }