31 lines
1.2 KiB
Swift
31 lines
1.2 KiB
Swift
import Foundation
|
|
|
|
struct RetryPolicy {
|
|
let maxAttempts: Int
|
|
let baseDelay: TimeInterval
|
|
let maxDelay: TimeInterval
|
|
let shouldRetry: (Error) -> Bool
|
|
|
|
static let fileSave = RetryPolicy(maxAttempts: 4, baseDelay: 0.25, maxDelay: 4.0) { _ in true }
|
|
static let network = RetryPolicy(maxAttempts: 3, baseDelay: 0.5, maxDelay: 8.0) { error in
|
|
let retryable: Set<Int> = [NSURLErrorTimedOut, NSURLErrorNetworkConnectionLost,
|
|
NSURLErrorNotConnectedToInternet, NSURLErrorCannotConnectToHost]
|
|
return retryable.contains((error as NSError).code)
|
|
}
|
|
}
|
|
|
|
func withRetry<T>(policy: RetryPolicy, operation: @escaping () async throws -> T) async throws -> T {
|
|
var lastError: Error?
|
|
var delay = policy.baseDelay
|
|
for attempt in 1...policy.maxAttempts {
|
|
do { return try await operation() }
|
|
catch {
|
|
lastError = error
|
|
guard policy.shouldRetry(error), attempt < policy.maxAttempts else { break }
|
|
let jitter = Double.random(in: 0...0.3) * delay
|
|
try? await Task.sleep(for: .seconds(min(delay + jitter, policy.maxDelay)))
|
|
delay = min(delay * 2, policy.maxDelay)
|
|
}
|
|
}
|
|
throw lastError ?? URLError(.unknown)
|
|
}
|