Alamofire (4)—— 你需要知道的細(xì)節(jié)

??????Alamofire專題目錄,歡迎及時(shí)反饋交流 ??????


Alamofire 目錄直通車 --- 和諧學(xué)習(xí),不急不躁!


上一個(gè)篇章里面我們講解 SessionDelegate 是事件總響應(yīng)者,我們根據(jù)不同的需求 (DataTaskDelegate、DownloadTaskDelegate、UploadTaskDelegate、TaskDelegate),響應(yīng)總代理然后根據(jù)需求的不同交給專業(yè)的人去做專業(yè)的事。耦合性大大降低,架構(gòu)的分層更加明顯! 這個(gè)篇章我要介紹 Alamofire 一些非常好用的小細(xì)節(jié),幫助大家在日后的開(kāi)發(fā)里無(wú)往不利

一、SessionDelegate的對(duì)外閉包

我們的 SessionDelegate 不光是代理的總稱,同時(shí)也是我們對(duì)外邏輯強(qiáng)力輸出口,針對(duì)我們的代理響應(yīng)提供了非常之多的閉包~??

// 接受到挑戰(zhàn)回調(diào)
open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
// 后臺(tái)事件完成的回調(diào)閉包
open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?
// 任務(wù)完成的閉包
open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
// 下載讀寫的進(jìn)度閉包
open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
// 接收到事件任務(wù)數(shù)據(jù)的閉包
open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
// 接收到響應(yīng)的閉包
open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
  • 上面只是列舉了一些閉包,但是你可以通過(guò)閉包的名字可以非常清晰感知作用
  • 下面舉個(gè)??
// 創(chuàng)建request
SessionManager.default.request(urlStr)
// 監(jiān)聽(tīng)任務(wù)回調(diào)完成狀態(tài)
SessionManager.default.delegate.taskDidComplete = { (session,task,error) in
    print("任務(wù)完成了")
}

// 背后的邏輯
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 直接對(duì) taskDidComplete 的閉包聲明
  • 其實(shí)可以看到在 SessionDelegate 的代理響應(yīng)里面執(zhí)行 taskDidComplete 閉包
  • 這樣對(duì)外提供閉包的本質(zhì):就是對(duì)外提供能力,讓開(kāi)發(fā)人員更加自如,方便

二、動(dòng)態(tài)適配能力 - RequestAdapter

這個(gè)功能特別好用,能夠提供下面兩種能力。

  • 1: request 處理
  • 2: request 重定向

下面我們開(kāi)始來(lái)玩玩這個(gè)適配能力

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
    }
}
  • 實(shí)現(xiàn) RequestAdapter 協(xié)議的 adapt 方法
  • 對(duì)提供的 urlRequest 進(jìn)行處理,比如統(tǒng)一配置 token
  • 對(duì)urlRequest 重定向,換一個(gè)新的 request 請(qǐng)求.
  • 記住一定要配置:SessionManager.default.adapter = LGAdapter()

三、自定義驗(yàn)證

我們請(qǐng)求網(wǎng)絡(luò)習(xí)慣性 響應(yīng)狀態(tài)碼200多 就是正確,其實(shí)我們可以根據(jù)自己公司的特性自定義處理驗(yàn)證操作,更加符合實(shí)際開(kāi)發(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
}
  • 這段代碼里面我們?cè)诤竺骀準(zhǔn)教砑?validate 方法
  • 在閉包里面添加自己驗(yàn)證方式
  • 比如沒(méi)有 數(shù)據(jù)data 我就返回 10089 錯(cuò)誤
  • 狀態(tài)碼 == 404 的時(shí)候,我們返回 100800 錯(cuò)誤
  • 其他返回成功。自定義的驗(yàn)證根據(jù)自己特定需求處理,再一次感受到 Alamofire 的靈活

四、重試請(qǐng)求

  • 重試請(qǐng)求的操作可能大家平時(shí)在開(kāi)發(fā)里面運(yùn)用不多,但是我覺(jué)得也是有需求場(chǎng)景的。作為相似處理,放到這里跟大家講解非常合適
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)
            }
        }
    }
}
  • 當(dāng)SessionDelegate 完成請(qǐng)求的時(shí)候,判斷重試閉包是否存在,還有注意一定是錯(cuò)誤的情況,沒(méi)有錯(cuò)誤沒(méi)有必要重連。這里也透露出 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ù)遞歸就會(huì)發(fā)生很嚴(yán)重的影響
        completion(false,0)
    }
}
  • 實(shí)現(xiàn) RequestRetrier 協(xié)議的 should
  • 記得使用:SessionManager.default.retrier = LGAdapter().

五、Result

Alamofire 在請(qǐng)求數(shù)據(jù)會(huì)有一個(gè) Response 但是這個(gè)不是我們最終的結(jié)果,還需要進(jìn)過(guò)一層序列化。

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)過(guò)了 responseSerializer.serializeResponse 序列化處理的結(jié)果
  • result 的結(jié)果最終傳入到了 dataResponse
public enum Result<Value> {
    case success(Value)
    case failure(Error)
    
   // 提供成功還有失敗的校驗(yàn)
    public var isSuccess: Bool {... }
    public var isFailure: Bool {...}
    public var value: Value? {...}
    public var error: Error? {... }
}
  • 結(jié)果只有成功和失敗,設(shè)計(jì)成了枚舉,Swift 枚舉非常強(qiáng)大 ??????,這個(gè)地方也得以體現(xiàn)
  • 當(dāng)然,為了打印更加詳細(xì)的信息,使Result實(shí)現(xiàn)了 CustomStringConvertibleCustomDebugStringConvertible協(xié)議 :
extension Result: CustomStringConvertible {
    public var description: String {
       // 就是返回 "SUCCESS" 和 "FAILURE" 的標(biāo)識(shí)
    }
}
extension Result: CustomDebugStringConvertible {
    public var debugDescription: String {
        // 返回標(biāo)識(shí)的同時(shí),還返回了具體內(nèi)容   
    }
}
  • 下面還有很多其他方法的拓展,不是重點(diǎn)大家自己看看就OK

六、Timeline 時(shí)間軸

強(qiáng)大的Alamofire??????為了方便我們的開(kāi)發(fā)還給我們提供了 Timeline 時(shí)間軸, 大家可以通過(guò) Timeline 快速得到這個(gè)請(qǐng)求的時(shí)間數(shù)據(jù),從而判斷請(qǐng)求是否合理,是否需要優(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í)間軸的數(shù)據(jù)得到,這里我們的Alamofire設(shè)計(jì)了一個(gè)隊(duì)列
self.queue = {
    let operationQueue = OperationQueue()

    operationQueue.maxConcurrentOperationCount = 1
    operationQueue.isSuspended = true
    operationQueue.qualityOfService = .utility

    return operationQueue
}()
  • 同步隊(duì)列為了讓流程順序執(zhí)行。
  • 在剛初始化的時(shí)候當(dāng)前隊(duì)列是掛起的 operationQueue.isSuspended = true
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
   // 省略無(wú)關(guān)代碼,方便閱讀
   // 請(qǐng)求完成,隊(duì)列resume
   queue.isSuspended = false
}
  • 請(qǐng)求完成,隊(duì)列 resume
  • 看到這里也說(shuō)明了加入這個(gè)隊(duì)列的任務(wù)必然在請(qǐng)求完成之后

1:請(qǐng)求開(kāi)始時(shí)間

open func resume() {
    if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }
}

2:添加請(qǐng)求完成時(shí)間記錄

init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
    self.session = session
       // 省略無(wú)關(guān)代碼,方便閱讀
    delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}

3:初始化響應(yīng)時(shí)間

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
}

4:時(shí)間軸設(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:初始化記錄時(shí)間以及計(jì)算時(shí)間

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
}
  • 看到這里你也就知道為什么這些時(shí)間能夠記錄,是因?yàn)椴粩嗤ㄟ^(guò)隊(duì)列同步控制,在一些核心的點(diǎn)保存當(dāng)前時(shí)間! 比如:endTime
  • 還有一些關(guān)鍵核心時(shí)間比如:startTime , initialResponseTime 就是在相關(guān)代理里面設(shè)置的!
  • 如果沒(méi)有設(shè)置值,那么就在當(dāng)時(shí)調(diào)用的時(shí)候重置當(dāng)前時(shí)間
  • Timeline 的其他時(shí)間就是通過(guò)已知的 initialResponseTimerequestStartTimerequestCompletedTime 、serializationCompletedTime 計(jì)算得出!

這個(gè)篇章就先寫到這里吧!一不小心又是 01:40 ! 雖然這個(gè)點(diǎn)發(fā)出去,也不會(huì)有幾個(gè)人看了??????。但是我希望支持我的小伙伴??,睡一覺(jué)醒來(lái)自然而然就能接受到來(lái)自 Cooci 給你文章推送通知!一切的一切又是那么的美好??????,今夜睡去,期待美夢(mèng)之后的奮斗,加油~~~~~~??????

就問(wèn)此時(shí)此刻還有誰(shuí)?45度仰望天空,該死!我這無(wú)處安放的魅力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容