Быстрая асинхронная сеть. Как ПОВТОРИТЬ при ошибке?

Я создал общую сетевую функцию с использованием 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
}

Поместите ловушку внутри функции и бросайте только тогда, когда вы готовы бросить.

lorem ipsum 11.01.2023 13:11

Пожалуйста, извините за совершенно не относящееся к делу наблюдение: возможно, вы захотите еще раз подумать, действительно ли вы хотите повторить какой-либо ответ, отличный от 2xx. Например, если в веб-службе произошла внутренняя ошибка 5xx, вы действительно хотите повторить попытку? Мы не можем комментировать дальше, не зная, почему вы хотите повторить попытку, но повторная попытка для любого ответа, отличного от 200, может быть слишком широкой.

Rob 11.01.2023 19:49

Кроме того, еще одно несвязанное наблюдение: я бы настоятельно не советовал try? decoder.decode(…), потому что, если есть ошибка декодирования, вы отбрасываете важную диагностическую информацию, которая говорит вам, почему это не удалось. Лично я бы просто try и позволил выдать фактическую ошибку, которую предоставил JSONDecoder, или дал вашему decodingError связанное значение (возможно, исходную ошибку) с необходимой диагностикой. По мере усложнения ответов выяснить, почему декодирование не удалось, и так достаточно сложно; ошибка, выданная decode, точно скажет вам, в чем проблема.

Rob 11.01.2023 19:50

Спасибо за совет, я ценю это. В этом есть смысл. На самом деле я хочу повторить попытку только в том случае, если срок действия токена истек / я получаю 401. Глядя на это, я попал в кроличью нору, рассматривая возможность совершения нескольких вызовов в случае проблем с временной сетью swiftbysundell.com/articles/retrying-an-async -быстрая задача

Dan 11.01.2023 20:00
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вы хотите сделать это, я бы предложил написать представление 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)

Другие вопросы по теме