??????Alamofire專題目錄,歡迎及時反饋交流 ??????
Alamofire 目錄直通車 --- 和諧學習禽最,不急不躁淘钟!
上一個篇章里面我們講解
SessionDelegate
是事件總響應者御蒲,我們根據(jù)不同的需求(DataTaskDelegate、DownloadTaskDelegate蹦狂、UploadTaskDelegate痊乾、TaskDelegate)
喘批,響應總代理然后根據(jù)需求的不同交給專業(yè)的人去做專業(yè)的事撩荣。耦合性大大降低,架構(gòu)的分層更加明顯! 這個篇章我要介紹Alamofire
一些非常好用的小細節(jié)饶深,幫助大家在日后的開發(fā)里無往不利
一婿滓、SessionDelegate的對外閉包
我們的 SessionDelegate
不光是代理的總稱,同時也是我們對外邏輯強力輸出口粥喜,針對我們的代理響應提供了非常之多的閉包~??
// 接受到挑戰(zhàn)回調(diào)
open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
// 后臺事件完成的回調(diào)閉包
open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?
// 任務完成的閉包
open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
// 下載讀寫的進度閉包
open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
// 接收到事件任務數(shù)據(jù)的閉包
open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
// 接收到響應的閉包
open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
- 上面只是列舉了一些閉包凸主,但是你可以通過閉包的名字可以非常清晰感知作用
- 下面舉個??
// 創(chuàng)建request
SessionManager.default.request(urlStr)
// 監(jiān)聽任務回調(diào)完成狀態(tài)
SessionManager.default.delegate.taskDidComplete = { (session,task,error) in
print("任務完成了")
}
// 背后的邏輯
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
/// Executed after it is determined that the request is not going to be retried
let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
guard let strongSelf = self else { return }
// 回調(diào)完成閉包
strongSelf.taskDidComplete?(session, task, error)
}
// 其他邏輯省略。额湘。卿吐。
}
- 可以看到我們是可以直接從
SessionManager.default.delegate
直接對taskDidComplete
的閉包聲明 - 其實可以看到在
SessionDelegate
的代理響應里面執(zhí)行taskDidComplete
閉包 - 這樣對外提供閉包的本質(zhì):就是對外提供能力,讓開發(fā)人員更加自如锋华,方便
二嗡官、動態(tài)適配能力 - RequestAdapter
這個功能特別好用,能夠提供下面兩種能力毯焕。
- 1: request 處理
- 2: request 重定向
下面我們開始來玩玩這個適配能力
class LGAdapter: RequestAdapter{
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
// token
// 1: request 處理
// 2: request 重定向
var request = urlRequest
request.setValue("lgcoociToken", forHTTPHeaderField: "lgtoken")
let newUrlRequest = URLRequest.init(url: URL(string: "http://www.douban.com/j/app/radio/channels")!)
return newUrlRequest
}
}
- 實現(xiàn)
RequestAdapter
協(xié)議的adapt
方法 - 對提供的
urlRequest
進行處理衍腥,比如統(tǒng)一配置token
- 對
urlRequest
重定向,換一個新的request
請求. - 記住一定要配置:
SessionManager.default.adapter = LGAdapter()
三纳猫、自定義驗證
我們請求網(wǎng)絡(luò)習慣性 響應狀態(tài)碼200多
就是正確婆咸,其實我們可以根據(jù)自己公司的特性自定義處理驗證操作,更加符合實際開發(fā)
SessionManager.default.request(urlStr, method: .get, parameters: ["username":"Kody","password":"888888"])
.response { (response) in
debugPrint(response)
}.validate { (request, response, data) -> Request.ValidationResult in
guard let _ = data else{
return .failure(NSError.init(domain: "lgcooci", code: 10089, userInfo: nil))
}
let code = response.statusCode
if code == 404 {
return .failure(NSError.init(domain: "lgcooci", code: 100800, userInfo: nil))
}
return .success
}
- 這段代碼里面我們在后面鏈式添加
validate
方法 - 在閉包里面添加自己驗證方式
- 比如沒有
數(shù)據(jù)data
我就返回10089
錯誤 -
狀態(tài)碼 == 404
的時候芜辕,我們返回100800
錯誤 - 其他返回成功尚骄。自定義的驗證根據(jù)自己特定需求處理,再一次感受到
Alamofire
的靈活
四侵续、重試請求
- 重試請求的操作可能大家平時在開發(fā)里面運用不多倔丈,但是我覺得也是有需求場景的。作為相似處理状蜗,放到這里跟大家講解非常合適
if let retrier = retrier, let error = error {
retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
guard shouldRetry else { completeTask(session, task, error) ; return }
DispatchQueue.utility.after(timeDelay) { [weak self] in
guard let strongSelf = self else { return }
let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false
if retrySucceeded, let task = request.task {
strongSelf[task] = request
return
} else {
completeTask(session, task, error)
}
}
}
}
- 當
SessionDelegate
完成請求的時候需五,判斷重試閉包是否存在,還有注意一定是錯誤的情況轧坎,沒有錯誤沒有必要重連宏邮。這里也透露出retrier
搭配validate
更美哦 -
retrier
也是繼承協(xié)議的處理方式,操作參考adapter
extension LGAdapter: RequestRetrier{
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
print("manager = \(manager)")
print("request = \(request)")
print("error = \(error)")
completion(true,1)
// 一定要有出口,不然持續(xù)遞歸就會發(fā)生很嚴重的影響
completion(false,0)
}
}
- 實現(xiàn)
RequestRetrier
協(xié)議的should
- 記得使用:
SessionManager.default.retrier = LGAdapter()
.
五蜀铲、Result
Alamofire
在請求數(shù)據(jù)會有一個 Response
但是這個不是我們最終的結(jié)果,還需要進過一層序列化属百。
public func response<T: DataResponseSerializerProtocol>(
queue: DispatchQueue? = nil,
responseSerializer: T,
completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
-> Self
{
delegate.queue.addOperation {
let result = responseSerializer.serializeResponse(
self.request,
self.response,
self.delegate.data,
self.delegate.error
)
var dataResponse = DataResponse<T.SerializedObject>(
request: self.request,
response: self.response,
data: self.delegate.data,
result: result,
timeline: self.timeline
)
}
return self
}
-
result
是經(jīng)過了responseSerializer.serializeResponse
序列化處理的結(jié)果 -
result
的結(jié)果最終傳入到了dataResponse
public enum Result<Value> {
case success(Value)
case failure(Error)
// 提供成功還有失敗的校驗
public var isSuccess: Bool {... }
public var isFailure: Bool {...}
public var value: Value? {...}
public var error: Error? {... }
}
- 結(jié)果只有成功和失敗记劝,設(shè)計成了枚舉,
Swift
枚舉非常強大 ??????族扰,這個地方也得以體現(xiàn) - 當然厌丑,為了打印更加詳細的信息,使Result實現(xiàn)了
CustomStringConvertible
和CustomDebugStringConvertible
協(xié)議 :
extension Result: CustomStringConvertible {
public var description: String {
// 就是返回 "SUCCESS" 和 "FAILURE" 的標識
}
}
extension Result: CustomDebugStringConvertible {
public var debugDescription: String {
// 返回標識的同時渔呵,還返回了具體內(nèi)容
}
}
- 下面還有很多其他方法的拓展怒竿,不是重點大家自己看看就OK
六、Timeline 時間軸
強大的Alamofire
??????為了方便我們的開發(fā)還給我們提供了 Timeline 時間軸
, 大家可以通過 Timeline
快速得到這個請求的時間數(shù)據(jù)扩氢,從而判斷請求是否合理耕驰,是否需要優(yōu)化....
timeline: Timeline: {
"Request Start Time": 588099247.070,
"Initial Response Time": 588099272.474,
"Request Completed Time": 588099272.475,
"Serialization Completed Time": 588099272.475,
"Latency": 25.404 secs,
"Request Duration": 25.405 secs,
"Serialization Duration": 0.000 secs,
"Total Duration": 25.405 secs
}
- 時間軸的數(shù)據(jù)得到,這里我們的
Alamofire
設(shè)計了一個隊列
self.queue = {
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
operationQueue.isSuspended = true
operationQueue.qualityOfService = .utility
return operationQueue
}()
- 同步隊列為了讓流程順序執(zhí)行录豺。
- 在剛初始化的時候當前隊列是掛起的
operationQueue.isSuspended = true
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
// 省略無關(guān)代碼朦肘,方便閱讀
// 請求完成,隊列resume
queue.isSuspended = false
}
- 請求完成双饥,隊列
resume
- 看到這里也說明了加入這個隊列的任務必然在請求完成之后
1:請求開始時間
open func resume() {
if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }
}
2:添加請求完成時間記錄
init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
self.session = session
// 省略無關(guān)代碼媒抠,方便閱讀
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}
3:初始化響應時間
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
}
4:時間軸設(shè)置
var timeline: Timeline {
let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent()
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
return Timeline(
requestStartTime: requestStartTime,
initialResponseTime: initialResponseTime,
requestCompletedTime: requestCompletedTime,
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
)
}
5:初始化記錄時間以及計算時間
public init(
requestStartTime: CFAbsoluteTime = 0.0,
initialResponseTime: CFAbsoluteTime = 0.0,
requestCompletedTime: CFAbsoluteTime = 0.0,
serializationCompletedTime: CFAbsoluteTime = 0.0)
{
self.requestStartTime = requestStartTime
self.initialResponseTime = initialResponseTime
self.requestCompletedTime = requestCompletedTime
self.serializationCompletedTime = serializationCompletedTime
self.latency = initialResponseTime - requestStartTime
self.requestDuration = requestCompletedTime - requestStartTime
self.serializationDuration = serializationCompletedTime - requestCompletedTime
self.totalDuration = serializationCompletedTime - requestStartTime
}
- 看到這里你也就知道為什么這些時間能夠記錄,是因為不斷通過隊列同步控制咏花,在一些核心的點保存當前時間! 比如:
endTime
- 還有一些關(guān)鍵核心時間比如:
startTime
,initialResponseTime
就是在相關(guān)代理里面設(shè)置的趴生! - 如果沒有設(shè)置值,那么就在當時調(diào)用的時候重置當前時間
-
Timeline
的其他時間就是通過已知的initialResponseTime
和requestStartTime
昏翰、requestCompletedTime
苍匆、serializationCompletedTime
計算得出!
這個篇章就先寫到這里吧棚菊!一不小心又是
01:40
! 雖然這個點發(fā)出去锉桑,也不會有幾個人看了??????。但是我希望支持我的小伙伴??窍株,睡一覺醒來自然而然就能接受到來自 Cooci 給你文章推送通知民轴!一切的一切又是那么的美好??????,今夜睡去球订,期待美夢之后的奮斗后裸,加油~~~~~~??????就問此時此刻還有誰?45度仰望天空冒滩,該死微驶!我這無處安放的魅力!