Я работаю над созданием сетевого клиента для моего iOS приложения, использующего методы авторизации OAuth 2.0 (Access
& Refresh Token
). Для моего сетевого клиента есть функция, которую я пытался реализовать:
- Когда возникает ошибка 401, это означает, что
Access Token
истек, и мне нужно отправить Refresh Token
на мой сервер для получения нового Access Token
. - После получения нового
Access Token
мне нужно повторить предыдущий запрос, получивший ошибку 401.
Пока я написал это код для моего сетевого клиента:
typealias NetworkCompletion = Result<(Data, URLResponse), FRNetworkingError>
/// I am using a custom result type to support just an Error and not a Type object for success
enum NetworkResponseResult<Error> {
case success
case failure(Error)
}
class FRNetworking: FRNetworkingProtocol {
fileprivate func handleNetworkResponse(_ response: HTTPURLResponse) -> NetworkResponseResult<Error> {
switch response.statusCode {
case 200...299: return .success
case 401: return .failure(FRNetworkingError.invalidAuthToken)
case 403: return .failure(FRNetworkingError.forbidden)
case 404...500: return .failure(FRNetworkingError.authenticationError)
case 501...599: return .failure(FRNetworkingError.badRequest)
default: return .failure(FRNetworkingError.requestFailed)
}
}
func request(using session: URLSession = URLSession.shared, _ endpoint: Endpoint, completion: @escaping(NetworkCompletion) -> Void) {
do {
try session.dataTask(with: endpoint.request(), completionHandler: { (data, response, error) in
if let error = error {
print("Unable to request data \(error)")
// Invoke completion for error
completion(.failure(.unknownError))
} else if let data = data, let response = response {
// Passing Data and Response into completion for parsing in ViewModels
completion(.success((data, response)))
}
}).resume()
} catch {
print("Failed to execute request", error)
completion(.failure(.requestFailed))
}
}
}
Конечная точка - это просто структура, которая создает URLRequest:
struct Endpoint {
let path: String
let method: HTTPMethod
let parameters: Parameters?
let queryItems: [URLQueryItem]?
let requiresAuthentication: Bool
var url: URL? {
var components = URLComponents()
components.scheme = "http"
components.host = "127.0.0.1"
components.port = 8000
components.path = "/api\(path)"
components.queryItems = queryItems
return components.url
}
func request() throws -> URLRequest {
/// Creates a request based on the variables per struct
}
}
Куда поместить код, позволяющий FRNetworking.request()
получить новый токен и повторить запрос?
Я сделал следующее внутри оператора else if let data = data, let response = response
:
if let response = response as? HTTPURLResponse {
let result = self.handleNetworkResponse(response)
switch result {
case .failure(FRNetworkingError.invalidAuthToken):
break
// TODO: Get new Access Token and refresh?
default:
break
}
}
Это правильный подход, чтобы обновить sh токен и повторить вызов API или есть лучший способ?