Browse Questions

Answer: Wrap the request in a loop with a limited retry count. Optionally add exponential backoff — increasing delay between retries to avoid hammering the server.

Code Example:

func fetchWithRetry<T: Decodable>(
    _ type: T.Type,
    from url: URL,
    maxRetries: Int = 3
) async throws -> T {
    var lastError: Error?

    for attempt in 0..<maxRetries {
        do {
            return try await fetch(type, from: url)
        } catch {
            lastError = error

            // Don't retry on client errors (4xx)
            if let apiError = error as? APIError,
               case .unauthorized = apiError { throw error }

            // Exponential backoff: 1s, 2s, 4s...
            let delay = UInt64(pow(2.0, Double(attempt))) * 1_000_000_000
            try await Task.sleep(nanoseconds: delay)
        }
    }

    throw lastError ?? APIError.unknown
}