Here's a general solution that can be applied to any asynchronous function that has no parameters, except for callbacks. I simplified the logic, having only success and failure , progress should not be so complicated.
So, suppose your function looks like this:
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() }) }
The corresponding retry function might look like this:
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) } }) }
and can be called like this:
retry(times: 3, task: startUploading, success: { print("Succeeded") }, failure: { err in print("Failed: \(err)") })
Above will repeat the startUploading call three times if it continues to fail, otherwise it will stop on the first success.
Edit Functions that have other parameters can simply be built into the closure:
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)") } )