協(xié)議介紹
主要使用了Alamofire4.0新增的兩個(gè)協(xié)議RequestRetrier和RequestAdapter.
- RequestRetrier(請求重連)
public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void
public protocol RequestRetrier {
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
}
它可以在Request 遇到 Error 的時(shí)候, 在指定的延遲之后重新發(fā)起請求.
注意事項(xiàng):為了避免無限重連,需要對請求地址做最大重連限制
class OAuth2Handler: RequestAdapter, RequestRetrier {
public func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: RequestRetryCompletion) {
if let response = request.task.response as? HTTPURLResponse, response.statusCode == 401 {
completion(true, 1.0) // 1秒后重試
} else {
completion(false, 0.0) // 不重連
}
}
}
let sessionManager = SessionManager()
sessionManager.retrier = OAuth2Handler()
sessionManager.request(urlString).responseJSON { response in
debugPrint(response)
}
- RequestAdapter(請求適配器)
public protocol RequestAdapter {
func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}
它可以讓每一個(gè) SessionManager 生成的 Request 都在生成之前被解析并且按照規(guī)則適配. 一個(gè)使用適配器很典型的場景就是給請求添加一個(gè) Authorization 的請求頭.
class AccessTokenAdapter: RequestAdapter {
private let accessToken: String
init(accessToken: String) {
self.accessToken = accessToken
}
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var urlRequest = urlRequest
if urlRequest.urlString.hasPrefix("https://httpbin.org") {
urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
}
return urlRequest
}
}
let sessionManager = SessionManager()
sessionManager.adapter = AccessTokenAdapter(accessToken: "1234")
sessionManager.request("https://httpbin.org/get")
如果一個(gè) Error 在適配過程中產(chǎn)生的話, 它會逐層拋出, 最后傳遞到 Request 的請求回調(diào)里.
封裝HTTPSessionManager
注意事項(xiàng):
RequestRetrier協(xié)議的回調(diào)是請求失敗后才會進(jìn)入,如果是請求成功后,需要對個(gè)別錯(cuò)誤碼進(jìn)行重連操作,可以參考validate
import Alamofire
import SwiftyJSON
/// 數(shù)據(jù)回調(diào)
public typealias HTTPSuccessBlock = (_ result : JSON) -> Void
public typealias HTTPFailureBlock = (_ error : HTTPErrorCode) -> Void
public enum HTTPErrorCode : Int, Error {
case SystemError = 4000 //參數(shù)錯(cuò)誤
case UnknownError = 5000 //未知錯(cuò)誤
.
.
.
}
class HTTPSessionManager: NSObject {
/// Alamofire
private var manager : SessionManager {
let manager = SessionManager.default
manager.retrier = OAuth2Handler()
manager.adapter = OAuth2Handler()
return manager
}
}
extension HTTPSessionManager {
public func request(methodType : HTTPMethod, urlString : String, parameters : [String: Any]?, successBlock : @escaping HTTPSuccessBlock, failureBlock : @escaping HTTPFailureBlock) {
manager.request(urlString, method: methodType, parameters: requestJson, encoding: JSONEncoding.default, headers: SessionManager.defaultHTTPHeaders)
.responseJSON { (json) in
// 返回?cái)?shù)據(jù)處理
switch json.result {
case .success(let result):
let retCode = JSON(result)["retCode"].intValue
if retCode == 成功 {
successBlock(JSON(result))
} else {
guard let errorCode = HTTPErrorCode(rawValue: retCode) else {
return failureBlock(HTTPErrorCode.UnknownError)
}
return failureBlock(errorCode)
}
case .failure(let error):
failureBlock(HTTPErrorCode.UnknownError)
}
}
}
}