Я создал общую сетевую функцию с использованием async, которая работает нормально, но я хотел бы иметь возможность повторить запрос, если он выдает ошибку, например, если срок действия токена истек. Как мне это сделать, не меняя весь мой DO Catch?
func request<T: Codable>(_ endpoint: Endpoint) async throws -> T {
guard let url = endpoint.url else {
throw NetworkError.badURL
}
guard let token = idToken else {
throw NetworkError.invalidToken
}
let request = buildRequest(from: url, methodType: endpoint.methodType, idToken: token)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
//RETRY X AMOUNT OF TIMES <<<
throw NetworkError.invalidResponse
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
guard let result = try? decoder.decode(T.self, from: data) else {
throw NetworkError.decodingError
}
print("Result: \(result)")
return result
}
Пожалуйста, извините за совершенно не относящееся к делу наблюдение: возможно, вы захотите еще раз подумать, действительно ли вы хотите повторить какой-либо ответ, отличный от 2xx. Например, если в веб-службе произошла внутренняя ошибка 5xx, вы действительно хотите повторить попытку? Мы не можем комментировать дальше, не зная, почему вы хотите повторить попытку, но повторная попытка для любого ответа, отличного от 200, может быть слишком широкой.
Кроме того, еще одно несвязанное наблюдение: я бы настоятельно не советовал try? decoder.decode(…)
, потому что, если есть ошибка декодирования, вы отбрасываете важную диагностическую информацию, которая говорит вам, почему это не удалось. Лично я бы просто try
и позволил выдать фактическую ошибку, которую предоставил JSONDecoder
, или дал вашему decodingError
связанное значение (возможно, исходную ошибку) с необходимой диагностикой. По мере усложнения ответов выяснить, почему декодирование не удалось, и так достаточно сложно; ошибка, выданная decode
, точно скажет вам, в чем проблема.
Спасибо за совет, я ценю это. В этом есть смысл. На самом деле я хочу повторить попытку только в том случае, если срок действия токена истек / я получаю 401. Глядя на это, я попал в кроличью нору, рассматривая возможность совершения нескольких вызовов в случае проблем с временной сетью swiftbysundell.com/articles/retrying-an-async -быстрая задача
Если вы хотите сделать это, я бы предложил написать представление data(for:delegate:)
, которое обертывает логику повторных попыток, например:
extension URLSession {
func data(for request: URLRequest, delegate: URLSessionTaskDelegate? = nil, maxRetries: Int) async throws -> (Data, URLResponse) {
for _ in 0 ..< maxRetries {
let (data, response) = try await data(for: request, delegate: delegate)
guard let response = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
if 200..<300 ~= response.statusCode { // as an aside, note, any 2xx response is success
return (data, response)
}
}
throw NetworkError.invalidResponse
}
}
Затем вы можете заменить:
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
//RETRY X AMOUNT OF TIMES <<<
throw NetworkError.invalidResponse
}
С:
let (data, response) = try await URLSession.shared.data(for: request, maxRetries: 3)
Поместите ловушку внутри функции и бросайте только тогда, когда вы готовы бросить.