Вам нужны проверки Alamofire RequestRetrier и RequestAdapter здесь
Вот мой пример:
import UIKit
import Alamofire
class MyRequestAdapter: RequestAdapter, RequestRetrier {
private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?) -> Void
private let lock = NSLock()
private var isRefreshing = false
private var requestsToRetry: [RequestRetryCompletion] = []
var accessToken:String? = nil
var refreshToken:String? = nil
static let shared = MyRequestAdapter()
private init(){
let sessionManager = Alamofire.SessionManager.default
sessionManager.adapter = self
sessionManager.retrier = self
}
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var urlRequest = urlRequest
if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(BASE_URL), !urlString.hasSuffix("/renew") {
if let token = accessToken {
urlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
}
return urlRequest
}
// MARK: - RequestRetrier
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
lock.lock() ; defer { lock.unlock() }
if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 {
requestsToRetry.append(completion)
if !isRefreshing {
refreshTokens { [weak self] succeeded, accessToken in
guard let strongSelf = self else { return }
strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() }
if let accessToken = accessToken {
strongSelf.accessToken = accessToken
}
strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) }
strongSelf.requestsToRetry.removeAll()
}
}
} else {
completion(false, 0.0)
}
}
// MARK: - Private - Refresh Tokens
private func refreshTokens(completion: @escaping RefreshCompletion) {
guard !isRefreshing else { return }
isRefreshing = true
let urlString = "\(BASE_URL)token/renew"
Alamofire.request(urlString, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: ["Authorization":"Bearer \(refreshToken!)"]).responseJSON { [weak self] response in
guard let strongSelf = self else { return }
if
let json = response.result.value as? [String: Any],
let accessToken = json["accessToken"] as? String
{
completion(true, accessToken)
} else {
completion(false, nil)
}
strongSelf.isRefreshing = false
}
}
}
Мой пример немного сложен, но дав общем, у нас есть два важных метода: первый - adapt(_ urlRequest: URLRequest) throws -> URLRequest
, где мы присоединяем токен, здесь у меня есть собственная логика, в которой одна из служб не должна прикреплять этот токен в качестве заголовка.Второй метод - func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
, где я проверяю, какой код ошибки (в моем примере 401).И затем я обновляю свои токены с помощью
private func refreshTokens(completion: @escaping RefreshCompletion)
В моем случае у меня есть токен обновления и токен доступа, и когда я вызываю службу с токеном обновления, я не должен добавлять свой старый токен доступа в заголовок.Я думаю, что это не лучшая практика, но она была реализована из людей, которых я не знаю.