Быстрая "повторная" логика по запросу

Итак, я немного потерял, как реализовать логику повтора, когда мой запрос на загрузку завершился неудачей.

Вот мой код, я бы хотел, чтобы некоторые рекомендации о том, как это сделать

func startUploading(failure failure: (NSError) -> Void, success: () -> Void, progress: (Double) -> Void) {
        DDLogDebug("JogUploader: Creating jog: \(self.jog)")

        API.sharedInstance.createJog(self.jog,
            failure: { error in
                failure(error)
            }, success: {_ in
                success()
        })
    }

Ответ 1

Здесь общее решение, которое может быть применено к любой асинхронной функции, которая не имеет параметров, за исключением обратных вызовов. Я упростил логику, имея только success и failure, progress не должен быть таким сложным.

Итак, предположим, что ваша функция выглядит следующим образом:

func startUploading(success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
    DDLogDebug("JogUploader: Creating jog: \(self.jog)")

    API.sharedInstance.createJog(self.jog,
        failure: { error in
            failure(error)
        }, success: {_ in
            success()
    })
}

Соответствующая функция retry может выглядеть так:

func retry(times: Int, task: (success: @escaping () -> Void, failure: @escaping (Error) -> Void) -> Void, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
    task(success: success, 
        failure: { error in
            // do we have retries left? if yes, call retry again
            // if not, report error
            if times > 0 {
                retry(times - 1, task: task, success: success, failure: failure)
            } else {
                failure(error)
            }
        })
}

и можно назвать так:

retry(times: 3, task: startUploading,
    success: {
        print("Succeeded")
    },
    failure: { err in
        print("Failed: \(err)")
})

Выше будет повторять вызов startUploading три раза, если он продолжает сбой, в противном случае остановится при первом успехе.

Редактировать Функции, которые имеют другие параметры, могут быть просто встроены в замыкание:

func updateUsername(username: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
    ...
}

retry(times: 3, { success, failure in updateUsername(newUsername, success, failure) },
    success: {
        print("Updated username")
    },
    failure: {
        print("Failed with error: \($0)")
    }
)

Ответ 2

Вот обновленный ответ для быстрого 3. Я также добавил общий объект в блок успеха, поэтому, если вы сделаете объект после завершения сетевого вызова, вы можете передать его до окончательного закрытия. Вот функция повтора:

func retry<T>(_ attempts: Int, task: @escaping (_ success: @escaping (T) -> Void, _ failure: @escaping (Error) -> Void) -> Void, success: @escaping (T) -> Void, failure: @escaping (Error) -> Void) {
task({ (obj) in
  success(obj)
}) { (error) in
  print("Error retry left \(attempts)")
  if attempts > 1 {
    self.retry(attempts - 1, task: task, success: success, failure: failure)
  } else {
      failure(error)
    }
  }
}

И вот как вы могли бы использовать его, если бы вы обновили пользователя и хотели вернуть новый пользовательский объект с обновленной информацией:

NetworkManager.shared.retry(3, task: { updatedUser, failure in
NetworkManager.shared.updateUser(user, success: updatedUser, error: failure) }
, success: { (updatedUser) in
  print(updatedUser.debugDescription)
}) { (err) in
  print(err)
}