Alamofire(三) 實(shí)戰(zhàn)分析

前言

前面分析了一波Request的整個(gè)流程和源碼,本文是講解到底在項(xiàng)目中如何實(shí)際使用,來滿足對(duì)于網(wǎng)絡(luò)請(qǐng)求的各種需求。

Adapter動(dòng)態(tài)適配

 SessionManager.default.adapter = DLAdapter()
        SessionManager.default.request(myGetUrlString, method: .get, parameters: ["array":getJsonFromArray(array)])
            .response { (response) in
                debugPrint(response)
        }

class DLAdapter: RequestAdapter{
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        var request = urlRequest
        request.setValue("token_value", forHTTPHeaderField: "token");
        return request;
    }
}
  • 實(shí)現(xiàn)RequestAdapter,實(shí)現(xiàn)adapt方法,在這里面做了設(shè)置請(qǐng)求頭token的操作
  • SessionManager.default.adapter = DLAdapter(),配置了Adapter后,所有的請(qǐng)求,都會(huì)帶上個(gè)這個(gè)請(qǐng)求頭的值
  • 還可以實(shí)現(xiàn)重定向功能等等,具體想統(tǒng)一對(duì)request做什么處理,要看項(xiàng)目的具體需求了
源碼探索

探索里面的源碼實(shí)現(xiàn),我這里是把幾處的源碼都綜合在一處了

open var adapter: RequestAdapter?
   
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)

let urlRequest = try self.urlRequest.adapt(using: adapter)

func adapt(using adapter: RequestAdapter?) throws -> URLRequest {
        guard let adapter = adapter else { return self }
        return try adapter.adapt(self)
}

public protocol RequestAdapter {
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}

在創(chuàng)建task的時(shí)候會(huì)傳遞當(dāng)前的adapter,這個(gè)adapter在這里try adapter.adapt(self)會(huì)執(zhí)行,而且這個(gè)adapter找不到具體的實(shí)現(xiàn),它是外界直接傳遞進(jìn)來的,外界傳遞了則使用,外界不傳遞則不使用。

Retrier請(qǐng)求重試

使用方法很簡(jiǎn)單,和上面基本一樣,實(shí)現(xiàn)RequestRetrier協(xié)議里的should方法即可

SessionManager.default.retrier = DLAdapter()
class DLAdapter: RequestAdapter,RequestRetrier{
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        var request = urlRequest
        request.setValue("token_value", forHTTPHeaderField: "token");
        return request;
    }
    
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
        completion(true,1)
    }
}

  • 可以在should方法里根據(jù)條件判斷是返回true還是false,true則表示需要重試
  • 在使用的時(shí)候只要設(shè)置SessionManager.default.retrier即可
  • 實(shí)用的地方很多,比如某個(gè)接口需要輪詢重復(fù)查詢,這種情況用上retrier很方便
源碼探索

找到使用到retrier的方法

  open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
  /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
/// should be retried. Otherwise, complete the task by notifying the task delegate.
  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)
                    }
                }
            }
        } else {
            completeTask(session, task, error)
        }
}
  • 看注釋的說明是,如果錯(cuò)誤發(fā)生并且設(shè)置了retrier,會(huì)同步詢問request是否需要retrier,沒發(fā)生錯(cuò)誤就通知taskDelegate請(qǐng)求完成。
  • 這里面的方法就相當(dāng)于一個(gè)遞歸,如果retrySucceeded一直為ture,則會(huì)一直調(diào)用下去,所以我們要注意在should方法中要有返回為false的情況,不然會(huì)一直遞歸下去

自定義驗(yàn)證validate

在實(shí)際相關(guān)項(xiàng)目中,我們可能有一些響應(yīng)碼需要特殊處理,這時(shí)候自定義驗(yàn)證就很適合使用了,如果能配上上面的retrier更加好用,返回error后再retrier

SessionManager.default.request(myGetUrlString, method: .get, parameters: ["array":getJsonFromArray(array)])
            .response { (response) in
                debugPrint(response)
            }.validate { (request, response, data) -> Request.ValidationResult in
                guard let _ = data else{
                    return .failure(NSError.init(domain: "test", code: 10089, userInfo: nil))
                }
                let code = response.statusCode
                if code == 404 {
                    return .failure(NSError.init(domain: "test2", code: 100800, userInfo: nil))
                }
                return .success
        }    

這里是自定義了兩個(gè)error,在data為空和statusCode為404的時(shí)候都會(huì)返回自定義的error信息

    public func validate(_ validation: @escaping Validation) -> Self {
        let validationExecution: () -> Void = { [unowned self] in
            if
                let response = self.response,
                self.delegate.error == nil,
                case let .failure(error) = validation(self.request, response, self.delegate.data)
            {
                self.delegate.error = error
            }
        }

        validations.append(validationExecution)

        return self
    }

這里是把驗(yàn)證的閉包加到了validations里,然后在請(qǐng)求完成后,會(huì)把validations里的閉包都執(zhí)行一遍

    open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
      // Run all validations on the request before checking if an error occurred
        request.validations.forEach { $0() }
    }

TimeLine

Alamofire提供了TimeLine時(shí)間線,為了我們開發(fā)的便捷,能夠通過Timeline快速得到這個(gè)請(qǐng)求的時(shí)間數(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 
 }    

請(qǐng)求開始時(shí)間

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

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

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

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

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

時(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()
    )
}

初始化記錄時(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
}

Result

Alamofire請(qǐng)求完數(shù)據(jù)后會(huì)返回一個(gè)response,但是這個(gè)不是我們最終的結(jié)果,還需要進(jìn)行一些處理,才能返回成我們需要的。

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
}

可以看到這個(gè)result是在經(jīng)過responseSerializer.serializeResponse序列化后返回的,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? {... }
}

Result的枚舉值只有success和failure,使用起來非常方便

總結(jié)

以上都是比較實(shí)用的Alamofire開發(fā)技巧,希望大家能熟練使用!

最后編輯于
?著作權(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)容