Alamofire源碼學習(十二): 響應與解析

往期導航:

Alamofire源碼學習目錄合集json

簡介

響應與解析由兩個核心模塊組成:swift

  • Response.swift
    定義了兩個泛型結構體:DataResponseDownloadResponse來封裝請求的響應,包括:URLRequest, HTTPURLResponse, 響應原始數據(Data格式),序列化後的數據,請求指標數據。DownloadResponse還包括下載文件url,斷點續傳數據信息等數據
    做用僅僅是封裝,以及對序列化後的數據或錯誤進行了變換操做,用來在請求的完成回調做爲參數使用。
  • ResponseSerialization.swift 把請求到的數據進行序列化,使用泛型協議來進行隔離,能夠自由指定序列化後的數據類型
    Alamofire內置了幾個序列化類型,能夠直接拿來使用,也能夠本身實現本身的序列化器
    在序列化數據以前,還支持先對原始的Data進行預處理,封裝爲接口DataPreprocessor,解析器能夠持有預處理協議對象,在開始解析data以前,先對data使用預處理器進行預處理以後,再進行解析。

Response請求響應封裝:

分爲兩種響應:api

  • 普通請求響應(包括DataRequest,UploadRequest)
  • 下載請求響應(數據會被保存到本地文件,所以會持有一個文件url,若碰到下載中斷,還會暫存已下載的數據供斷點續傳處理)

使用場景:
在使用Alamofire請求時,請求完成回調裏的參數,就是Response對象:數組

//發送請求,把響應數據解析爲JSOn
AF.request("").responseJSON(completionHandler: {
    (resp : AFDataResponse<Any>) in
    resp.data//原始響應Data數據(Data?類型)
    resp.value//序列化後的泛型對象(Any?類型)
    resp.error//序列化或者請求出錯的錯誤對象(AFError?類型)
})
複製代碼

定義:

兩個結構體都使用泛型封裝,泛型爲序列化的數據類型以及序列化中出的錯誤:服務器

public struct DataResponse<Success, Failure: Error> public struct DownloadResponse<Success, Failure: Error> //而後對這兩個類型進行了泛型包裝,把錯誤封裝爲AFError,用來在Alamofire請求時返回: public typealias AFDataResponse<Success> = DataResponse<Success, AFError> public typealias AFDownloadResponse<Success> = DownloadResponse<Success, AFError> 複製代碼

DataResponse

封裝響應頭, 響應數據, 以及響應對應的請求, 同時定義泛型Success與Error, 用來將響應數據或者錯誤序列化爲對應類型, 擴展了一大堆方法來對響應數據與錯誤進行變換
只用來封裝DataRequest跟UploadRequest的響應markdown

私有屬性以及初始化:
/// 請求
    public let request: URLRequest?

    /// 響應頭
    public let response: HTTPURLResponse?

    /// 響應原始數據
    public let data: Data?

    /// 最終的請求指標, 包括請求時間, 重定向次數等
    public let metrics: URLSessionTaskMetrics?

    /// 序列化響應花的時間
    public let serializationDuration: TimeInterval

    /// 序列化響應的結果
    public let result: Result<Success, Failure>

    /// 快速取得序列化的數據(可能爲nil)
    public var value: Success? { result.success }

    /// 快速取得序列化的錯誤
    public var error: Failure? { result.failure }

    public init(request: URLRequest?, response: HTTPURLResponse?, data: Data?, metrics: URLSessionTaskMetrics?, serializationDuration: TimeInterval, result: Result<Success, Failure>) {
        self.request = request
        self.response = response
        self.data = data
        self.metrics = metrics
        self.serializationDuration = serializationDuration
        self.result = result
    }
複製代碼
擴展實現String描述以及debugString描述

由於result屬性類型是Result泛型,所以在string描述直接把result的描述返回過去,無論類型是.success仍是.failure,都會自動轉爲對應參數類型的描述網絡

extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible {
    /// 轉爲String時的描述, 直接丟result的描述過去, 不論是成功仍是失敗
    public var description: String {
        "\(result)"
    }

    /// debug時的描述信息
    public var debugDescription: String {
        guard let urlRequest = request else { return "[Request]: None\n[Result]: \(result)" }

        let requestDescription = DebugDescription.description(of: urlRequest)

        let responseDescription = response.map { response in
            let responseBodyDescription = DebugDescription.description(for: data, headers: response.headers)

            return """ \(DebugDescription.description(of: response)) \(responseBodyDescription.indentingNewlines()) """
        } ?? "[Response]: None"

        let networkDuration = metrics.map { "\($0.taskInterval.duration)s" } ?? "None"

        return """ \(requestDescription) \(responseDescription) [Network Duration]: \(networkDuration) [Serialization Duration]: \(serializationDuration)s [Result]: \(result) """
    }
}
複製代碼
擴展添加類型變換

由於持有的result是一個保存了成功數據/錯誤信息的泛型Result,所以添加了四個變換方法,能夠返回一個新的DataResponse,成功錯誤類型能夠變換爲其餘類型:閉包

extension DataResponse {
    /// 1.用一個閉包把原result的Success類型變換爲新類型, 閉包transform不容許拋出錯誤, 不處理Failure狀況
    public func map<NewSuccess>(_ transform: (Success) -> NewSuccess) -> DataResponse<NewSuccess, Failure> {
        DataResponse<NewSuccess, Failure>(request: request,
                                          response: response,
                                          data: data,
                                          metrics: metrics,
                                          serializationDuration: serializationDuration,
                                          result: result.map(transform))
    }

    /// 2.用一個閉包把原result的Success類型變換成新類型, 閉包能夠拋出異常, 拋出異常時, 會封裝爲Failure返回給上層, 一樣不處理原result的Failure狀況
    public func tryMap<NewSuccess>(_ transform: (Success) throws -> NewSuccess) -> DataResponse<NewSuccess, Error> {
        DataResponse<NewSuccess, Error>(request: request,
                                        response: response,
                                        data: data,
                                        metrics: metrics,
                                        serializationDuration: serializationDuration,
                                        result: result.tryMap(transform))
    }

    /// 3.用閉包把原result的Failure狀況變換爲新的Failure, 對應上面的map
    public func mapError<NewFailure: Error>(_ transform: (Failure) -> NewFailure) -> DataResponse<Success, NewFailure> {
        DataResponse<Success, NewFailure>(request: request,
                                          response: response,
                                          data: data,
                                          metrics: metrics,
                                          serializationDuration: serializationDuration,
                                          result: result.mapError(transform))
    }

    /// 4.用閉包處理原result的Failure狀況, 能夠拋出異常, 對應上面tryMap
    public func tryMapError<NewFailure: Error>(_ transform: (Failure) throws -> NewFailure) -> DataResponse<Success, Error> {
        DataResponse<Success, Error>(request: request,
                                     response: response,
                                     data: data,
                                     metrics: metrics,
                                     serializationDuration: serializationDuration,
                                     result: result.tryMapError(transform))
    }
}
複製代碼

DownloadResponse

跟上面的DataResponse相似, 用來封裝響應, 不過只用來封裝DownloadRequest的響應, 由於須要封裝文件相關的數據,因此屬性多了下載文件相關的屬性app

屬性與初始化

對比DataResponse多了兩個屬性:async

public let request: URLRequest?
    
    public let response: HTTPURLResponse?

    /// 下載的文件地址
    public let fileURL: URL?

    /// 下載被取消時, 已經下載的數據, 用來傳給上層決定是否保存用以斷點續傳
    public let resumeData: Data?

    public let metrics: URLSessionTaskMetrics?

    public let serializationDuration: TimeInterval

    public let result: Result<Success, Failure>

    public var value: Success? { result.success }

    public var error: Failure? { result.failure }

    public init(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, resumeData: Data?, metrics: URLSessionTaskMetrics?, serializationDuration: TimeInterval, result: Result<Success, Failure>) {
        self.request = request
        self.response = response
        self.fileURL = fileURL
        self.resumeData = resumeData
        self.metrics = metrics
        self.serializationDuration = serializationDuration
        self.result = result
    }
複製代碼
其餘擴展

相似上面的DataResponse,DownloadResponse也有對應的兩個擴展,分別用來實現String描述,以及四個類型變換的方法,基本上一毛同樣,就再也不贅述

debug輔助類型與擴展

在兩個Response實現DebugString協議時,使用了兩個輔助類型:

  • 輔助枚舉DebugDescription,沒有任何case,只封裝了四個靜態方法,分別用來輸出相關的debug信息,調試時可使用debugPrint函數或者.debugDescription屬性來打印
private enum DebugDescription {
    static func description(of request: URLRequest) -> String {
        let requestSummary = "\(request.httpMethod!) \(request)"
        let requestHeadersDescription = DebugDescription.description(for: request.headers)
        let requestBodyDescription = DebugDescription.description(for: request.httpBody, headers: request.headers)

        return """ [Request]: \(requestSummary) \(requestHeadersDescription.indentingNewlines()) \(requestBodyDescription.indentingNewlines()) """
    }

    static func description(of response: HTTPURLResponse) -> String {
        """ [Response]: [Status Code]: \(response.statusCode) \(DebugDescription.description(for: response.headers).indentingNewlines()) """
    }

    static func description(for headers: HTTPHeaders) -> String {
        guard !headers.isEmpty else { return "[Headers]: None" }

        let headerDescription = "\(headers.sorted())".indentingNewlines()
        return """ [Headers]: \(headerDescription) """
    }

    static func description(for data: Data?, headers: HTTPHeaders, allowingPrintableTypes printableTypes: [String] = ["json", "xml", "text"], maximumLength: Int = 100_000) -> String {
        guard let data = data, !data.isEmpty else { return "[Body]: None" }

        guard
            data.count <= maximumLength,
            printableTypes.compactMap({ headers["Content-Type"]?.contains($0) }).contains(true)
        else { return "[Body]: \(data.count) bytes" }

        return """ [Body]: \(String(decoding: data, as: UTF8.self) .trimmingCharacters(in: .whitespacesAndNewlines) .indentingNewlines()) """
    }
}
複製代碼
  • 擴展String把普通換行變成帶有縮進的換行,簡單的替換
extension String {
    fileprivate func indentingNewlines(by spaceCount: Int = 4) -> String {
        let spaces = String(repeating: " ", count: spaceCount)
        return replacingOccurrences(of: "\n", with: "\n\(spaces)")
    }
}
複製代碼

ResponseSerialization響應序列化

Alamofire的一個核心爲Session+Request及其子類,管理Session,建立URLRequest,發送URLSessionTask,管理Request與Task的關聯,處理請求中的各個事件以及錯誤處理。當請求完成時,須要對響應數據進行解析,默承認以解析爲JSON,String,Decodable等格式,也能夠本身實現接口把響應數據解析爲其餘接口。所以另一個核心就是ResponseSerialization響應解析。整個網絡請求的流程中,核心就兩點:1.發送請求,2.處理請求

特色:

  • 對響應數據序列化使用接口抽象,接口很是簡單:入參爲:request,task,data,error,出參爲泛型序列化後的對象,接口只關心序列化的入參與出參,具體的序列化出來的數據類型與序列化方法由各個序列化器各自實現封裝。
  • 在序列化前,Alamofire還定義了一個DataPreprocessor接口,用來對原始響應數據線進行預處理操做,入參爲Data,出參也爲Data,且能夠拋出異常。Alamofire內置的幾個序列化器都持有了Data預處理器,在開始解析序列化數據前先對Data進行預處理,catch可能產生的錯誤並拋出,預處理完成後纔開始後續的序列化操做
  • 在使用Session建立Request以後,其實並無發送Task,發送task的時機有三個:
    1. Session.startImmediately = true,建立的request首次調用response***方法添加響應解析回調時,會再添加完成回調以後調用resume發送請求
    2. Session.startImmediately = true,建立的request爲DataStreamRequest,且建立完成後,調用了方法asInputStream()返回InputStream,在建立完IOStream對並綁定以後,就會馬上調用resume發送請求
    3. Session.startImmediately = false,則建立的request不會自動發送,須要在調用response***方法添加解析回調以後,手動調用request.resume()方法來發送請求
  • Alamofire內置了多個序列化器,每一個序列化器都有DataResponse與DownResponse兩種,每一個序列化器定義完成以後都跟着Request的擴展,爲Request添加對應的response方法
  • 對Request調用.response***方法本質上是添加了一個序列化處理回調,該回調會存在Request的MutableState屬性中的responseSerializers回調數組中,當請求完成後,request就會挨個調用序列化回調。所以,能夠對建立的一個Request進行屢次resonse操做,對同一個請求進行屢次解析,獲得不一樣類型的響應數據
  • DataStreamResponse的解析處理與DataResponse以及DownloadResponse不一樣,放在最後單獨說明

序列化接口定義

很是簡單,就一個泛型序列化數據類型,一個簡單的序列化方法,分爲Data與Download兩種,卻別在於:

  1. Data序列化的原數據類型爲Data?,Download序列化的原數據類型爲fileURL?
  2. 對Download的序列化操做,其實也能夠看作,先把fileURL用Data讀出來,而後用Data序列化方法來處理。所以若一個系列化器同時能夠序列化兩種task,就能夠直接對Download進行實現默認操做(後面會講)
/// 把請求+響應頭+響應Data+錯誤一塊兒使用接口的方法序列化成泛型響應對象
/// 只供DataResponse使用
public protocol DataResponseSerializerProtocol {
    /// 序列化後的數據類型
    associatedtype SerializedObject
    
    func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> SerializedObject
}

/// 給DownloadResponse請求使用
public protocol DownloadResponseSerializerProtocol {
    /// 序列化後的數據類型
    associatedtype SerializedObject
    
    func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> SerializedObject
}
複製代碼

組合序列化接口協議ResponseSerializer

將Data與Download兩種序列化協議組合在一塊兒,並加上了Data預處理協議對象,容許空Data的HTTPMethod與響應碼。

默認行爲下,若返回的響應Data爲空,會被當作請求失敗處理,可是實際上好比Head請求,沒有響應Data。響應碼爲204(No Content),205(Reset Content)也是隻有響應頭,沒有響應Data。可是不能當作請求失敗處理,所以ResponseSerializer添加了兩個Set屬性來存放容許響應Data爲空的HTTPMethod與響應碼。

/// 組合協議, 能同時序列化DataResponse與DownloadResponse
public protocol ResponseSerializer: DataResponseSerializerProtocol & DownloadResponseSerializerProtocol {
    /// Data預處理器, 用來在序列化以前對Data進行預處理
    var dataPreprocessor: DataPreprocessor { get }
    /// `HTTPMethod`s for which empty response bodies are considered appropriate.
    /// 容許響應data爲空的請求Method(默認HEAD請求的響應data爲nil)
    var emptyRequestMethods: Set<HTTPMethod> { get }
    /// 容許響應data爲空的錯誤碼(默認[204, 205]的響應碼data爲nil)
    var emptyResponseCodes: Set<Int> { get }
}
複製代碼

響應數據預處理協議DataPreprocessor

協議很簡單,就一個方法,入參出參均爲Data,實現對象能夠對入參Data進行預處理,執行增刪改查操做後,返回處理後的Data,再繼續進行解析,方法容許拋出異常,拋出異常時,上層會catch異常並封裝成AFError返回

/// 用來預處理Data的協議, Alamofire有兩個默認實現
public protocol DataPreprocessor {
    /// 出入參均爲Data, 容許拋出異常, 拋出異常時封裝爲AFError返回
    func preprocess(_ data: Data) throws -> Data
}
複製代碼

Alamofire內置的兩個響應數據預處理器

Alamofire實現了兩個預處理器struct,權限爲public,能夠直接使用

透傳預處理器PassthroughPreprocessor

沒作任何處理,只是把Data原樣傳遞回去,用來在實現ResponseSerializer協議時,做爲默認的預處理器使用

/// 默認透傳處理器, 不作任何處理
public struct PassthroughPreprocessor: DataPreprocessor {
    public init() {}
    public func preprocess(_ data: Data) throws -> Data { data }
}
複製代碼
谷歌的XSSI數據預處理器GoogleXSSIPreprocessor

去掉數據前綴的)]}',\n六個字符。只是定義該預處理器,Alamofire中並未使用。

/// 預處理谷歌的XSSI數據, 去掉前6個字符
public struct GoogleXSSIPreprocessor: DataPreprocessor {
    public init() {}

    public func preprocess(_ data: Data) throws -> Data {
        (data.prefix(6) == Data(")]}',\n".utf8)) ? data.dropFirst(6) : data
    }
}
複製代碼

擴展ResponseSerializer協議,添加默認實現

ResponseSerializer添加了三個屬性的默認實現,而且添加了三個快捷方法來判斷請求與相應是否容許響應數據爲空

/// 擴展協議, 給三個屬性添加默認實現
extension ResponseSerializer {
    /// 默認的Data預處理器爲透傳處理, 不作任何處理
    public static var defaultDataPreprocessor: DataPreprocessor { PassthroughPreprocessor() }
    /// 默認容許空Data的Method爲HEAD
    public static var defaultEmptyRequestMethods: Set<HTTPMethod> { [.head] }
    /// 默認容許空Data的狀態碼爲204, 205
    public static var defaultEmptyResponseCodes: Set<Int> { [204, 205] }

    /// 三個屬性的默認實現
    public var dataPreprocessor: DataPreprocessor { Self.defaultDataPreprocessor }
    public var emptyRequestMethods: Set<HTTPMethod> { Self.defaultEmptyRequestMethods }
    public var emptyResponseCodes: Set<Int> { Self.defaultEmptyResponseCodes }

    /// 檢測request是否容許響應Data爲空
    /// 使用的是可選類型的map跟flatMap方法, 可選類型的map跟flatMap的方法的參數爲數據類型, 返回的類型也是可選類型, 當值不爲nil時就會調用閉包, 爲nil時直接返回nil
    public func requestAllowsEmptyResponseData(_ request: URLRequest?) -> Bool? {
        request.flatMap { $0.httpMethod }
            .flatMap(HTTPMethod.init)
            .map { emptyRequestMethods.contains($0) }
    }

    /// 檢測響應的狀態碼是否容許Data爲空
    public func responseAllowsEmptyResponseData(_ response: HTTPURLResponse?) -> Bool? {
        response.flatMap { $0.statusCode }
            .map { emptyResponseCodes.contains($0) }
    }

    /// 組合上面兩個檢測方法
    public func emptyResponseAllowed(forRequest request: URLRequest?, response: HTTPURLResponse?) -> Bool {
        (requestAllowsEmptyResponseData(request) == true) || (responseAllowsEmptyResponseData(response) == true)
    }
}
複製代碼

對Download序列化協議進行擴展添加Data方式的解析實現

當某個序列化器,即實現了Data序列化接口以及Download序列化接口,那麼能夠把Download序列化的行爲看作:

  1. 先把文件用Data讀出來
  2. 再使用Data的序列化方法來處理

所以就能夠對Download解析方法提供默認實現。

要注意的是,直接把整個文件用Data讀到內存,要注意文件太大的時候,會爆內存。

/// 給即符合DownloadResponseSerializerProtocol又符合DataResponseSerializerProtocol協議的類型提供默認實現
/// 只要先把數據從文件裏讀出來就能夠做爲Data去實現DataResponseSerializerProtocol的序列化方法
/// 注意大文件直接讀出來會爆內存
extension DownloadResponseSerializerProtocol where Self: DataResponseSerializerProtocol {
    public func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> Self.SerializedObject {
        /// 有錯直接拋出, 丟給上層去catch
        guard error == nil else { throw error! }
        /// 文件url爲空拋出對應錯誤
        guard let fileURL = fileURL else {
            throw AFError.responseSerializationFailed(reason: .inputFileNil)
        }
        /// 讀取Data, catch讀取錯誤並轉換拋出
        let data: Data
        do {
            data = try Data(contentsOf: fileURL)
        } catch {
            throw AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))
        }
        /// 走DataResponseSerializerProtocol的序列化邏輯, catch錯誤並拋出
        do {
            return try serialize(request: request, response: response, data: data, error: error)
        } catch {
            throw error
        }
    }
}
複製代碼

定義內置序列化器與添加序列化方法

  • Alamofire對Request擴展添加了7種序列化方法,並提供了5種內置序列化器(fileUrl,Data, String, JSON, Decodable),能夠直接使用,也能夠根據接口本身實現定製的序列化器。
  • 每一個序列化器都包括定義類型,以及緊跟着定義後面的對DataRequest與DownloadRequest添加的擴展response***方法。
1.原始數據序列化(兩種)

由於原數據就是Data或者fileurl類型,所以就沒有再專門定義序列化器,而是直接對兩種Request添加了擴展序列化響應方法,解析回調也很簡單:

  1. 封裝AFResult類型,Success類型爲Data?或者URL?類型
  2. 組裝DataResponse,Success類型爲1中的AFResult
  3. 告知事件監聽器序列化響應完成
  4. 告知Request序列化響應完成
extension DataRequest {
    
    /// 普通解析, 只是封裝Data
    @discardableResult
    public func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDataResponse<Data?>) -> Void) -> Self {
        //appendResponseSerializer是一個參數爲閉包, 無返回值的方法, 尾隨閉包寫法第一眼看起來容易疑問
        appendResponseSerializer {
            // 開始解析響應, 必須在解析隊列完成
            // 默認不解析, 直接封裝下Data
            let result = AFResult<Data?>(value: self.data, error: self.error)
            // 響應解析完成

            // 解析完成後的行爲須要在對應隊列完成
            self.underlyingQueue.async {
                // 封裝
                let response = DataResponse(request: self.request,
                                            response: self.response,
                                            data: self.data,
                                            metrics: self.metrics,
                                            serializationDuration: 0,
                                            result: result)
                // 告知監聽器
                self.eventMonitor?.request(self, didParseResponse: response)
                // 回調解析完成
                self.responseSerializerDidComplete { queue.async { completionHandler(response) } }
            }
        }

        return self
    }
}

extension DownloadRequest {
    /// 普通處理下載響應, 下載數據以文件url形式回調處理, 文件url爲可選類型
    @discardableResult
    public func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDownloadResponse<URL?>) -> Void)
        -> Self {
        appendResponseSerializer {
            // Start work that should be on the serialization queue.
            let result = AFResult<URL?>(value: self.fileURL, error: self.error)
            // End work that should be on the serialization queue.

            self.underlyingQueue.async {
                let response = DownloadResponse(request: self.request,
                                                response: self.response,
                                                fileURL: self.fileURL,
                                                resumeData: self.resumeData,
                                                metrics: self.metrics,
                                                serializationDuration: 0,
                                                result: result)

                self.eventMonitor?.request(self, didParseResponse: response)

                self.responseSerializerDidComplete { queue.async { completionHandler(response) } }
            }
        }

        return self
    }
}
複製代碼
2.使用泛型序列化協議對象來處理響應(兩種)
  • 分別對應兩種響應,這個響應處理方法是最基本的處理方法,下面的幾個都是具現化了對應的序列化協議對象來調用該方法,若是使用本身實現的序列化器,也可使用這個方法來序列化數據,而不須要本身專門寫序列化方法。
  • 使用序列化協議對象進行處理數據時,須要在Request的serializationQueue隊列中執行並計算處理數據所花時間,處理數據完成後,回到underlyingQueue隊列進行後續操做
  • 序列化時接受的數據可能直接有錯誤(請求錯誤),也可能序列化過程當中出現錯誤,若原數據處理完成後的結果爲failure,就開始走重試邏輯
  • 最後執行完成相關回調
extension DataRequest {
    /// 使用自定義解析器解析
    @discardableResult
    public func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main, responseSerializer: Serializer, completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void)
        -> Self {
        appendResponseSerializer {
            // 開始解析響應, 必須在響應解析隊列完成, 由於有解析操做, 因此開始計時
            let start = ProcessInfo.processInfo.systemUptime
            // 用入參解析器解析數據, catch錯誤並轉換成AFError
            let result: AFResult<Serializer.SerializedObject> = Result {
                try responseSerializer.serialize(request: self.request,
                                                 response: self.response,
                                                 data: self.data,
                                                 error: self.error)
            }.mapError { error in
                error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error)))
            }
            // 用app啓動的時間差值來計算出解析所花時間
            let end = ProcessInfo.processInfo.systemUptime
            // 解析完成

            //回Request內部隊列來繼續處理
            self.underlyingQueue.async {
                //組裝DataResponse, Success類型爲序列化協議中的泛型SerializedObject類型
                let response = DataResponse(request: self.request,
                                            response: self.response,
                                            data: self.data,
                                            metrics: self.metrics,
                                            serializationDuration: end - start,
                                            result: result)
                
                //告知監聽器
                self.eventMonitor?.request(self, didParseResponse: response)

                guard let serializerError = result.failure, let delegate = self.delegate else {
                    // 若是解析成功, 直接走完成邏輯
                    self.responseSerializerDidComplete { queue.async { completionHandler(response) } }
                    return
                }
                // 解析出錯, 準備重試
                delegate.retryResult(for: self, dueTo: serializerError) { retryResult in
                    
                    // 是否完成的回調, nil表示要重試
                    var didComplete: (() -> Void)?

                    defer {
                        // 用defer來處理
                        if let didComplete = didComplete {
                            self.responseSerializerDidComplete { queue.async { didComplete() } }
                        }
                    }
                    
                    //根據參數retryResult判斷是否重試
                    switch retryResult {
                    case .doNotRetry:// 不重試, 直接完成
                        didComplete = { completionHandler(response) }

                    case let .doNotRetryWithError(retryError):// 不重試, 把error替換掉
                        // 用新的retryError初始化result
                        let result: AFResult<Serializer.SerializedObject> = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError"))

                        // 封裝新的Response
                        let response = DataResponse(request: self.request,
                                                    response: self.response,
                                                    data: self.data,
                                                    metrics: self.metrics,
                                                    serializationDuration: end - start,
                                                    result: result)

                        didComplete = { completionHandler(response) }

                    case .retry, .retryWithDelay:// 重試
                        delegate.retryRequest(self, withDelay: retryResult.delay)
                    }
                }
            }
        }

        return self
    }
}

extension DownloadRequest {
    /// 使用自定義序列化器來處理下載響應
    @discardableResult
    public func response<Serializer: DownloadResponseSerializerProtocol>(queue: DispatchQueue = .main, responseSerializer: Serializer, completionHandler: @escaping (AFDownloadResponse<Serializer.SerializedObject>) -> Void)
        -> Self {
        appendResponseSerializer {
            // 開始序列化結果, 計時
            let start = ProcessInfo.processInfo.systemUptime
            let result: AFResult<Serializer.SerializedObject> = Result {
                try responseSerializer.serializeDownload(request: self.request,
                                                         response: self.response,
                                                         fileURL: self.fileURL,
                                                         error: self.error)
            }.mapError { error in
                error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error)))
            }
            let end = ProcessInfo.processInfo.systemUptime
            // 序列化結果完成

            self.underlyingQueue.async {
                let response = DownloadResponse(request: self.request,
                                                response: self.response,
                                                fileURL: self.fileURL,
                                                resumeData: self.resumeData,
                                                metrics: self.metrics,
                                                serializationDuration: end - start,
                                                result: result)

                self.eventMonitor?.request(self, didParseResponse: response)

                guard let serializerError = result.failure, let delegate = self.delegate else {
                    self.responseSerializerDidComplete { queue.async { completionHandler(response) } }
                    return
                }
                // 重試
                delegate.retryResult(for: self, dueTo: serializerError) { retryResult in
                    var didComplete: (() -> Void)?

                    defer {
                        if let didComplete = didComplete {
                            self.responseSerializerDidComplete { queue.async { didComplete() } }
                        }
                    }

                    switch retryResult {
                    case .doNotRetry:
                        didComplete = { completionHandler(response) }

                    case let .doNotRetryWithError(retryError):
                        let result: AFResult<Serializer.SerializedObject> = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError"))

                        let response = DownloadResponse(request: self.request,
                                                        response: self.response,
                                                        fileURL: self.fileURL,
                                                        resumeData: self.resumeData,
                                                        metrics: self.metrics,
                                                        serializationDuration: end - start,
                                                        result: result)

                        didComplete = { completionHandler(response) }

                    case .retry, .retryWithDelay:
                        delegate.retryRequest(self, withDelay: retryResult.delay)
                    }
                }
            }
        }

        return self
    }
}
複製代碼
3.下載文件URL處理(僅DownloadResonse可用)
  • 只是一個簡單的序列化器,僅DownloadResonse用來處理fileUrl使用
  • 由於默認的DownloadResponse中fileUrl爲可選類型,能夠爲nil,所以使用該序列化器後,可將nil的狀況過濾,拋出AFError,返回不可選類型的fileURL。
  • 擴展DownloadRequest添加了responseURL方法
public struct URLResponseSerializer: DownloadResponseSerializerProtocol {
    
    public init() {}

    public func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> URL {
        guard error == nil else { throw error! }

        guard let url = fileURL else {
            // 文件url爲nil的時候, 拋出error
            throw AFError.responseSerializationFailed(reason: .inputFileNil)
        }

        return url
    }
}

extension DownloadRequest {
    /// 普通處理下載響應, 下載數據以文件url形式回調處理, 文件url必定不爲nil
    @discardableResult
    public func responseURL(queue: DispatchQueue = .main, completionHandler: @escaping (AFDownloadResponse<URL>) -> Void) -> Self {
        response(queue: queue, responseSerializer: URLResponseSerializer(), completionHandler: completionHandler)
    }
}
複製代碼
4.Data格式序列化器(兩種)
  • 定義DataResponseSerializer序列化器實現ResponseSerializer協議並修飾爲final,不可繼承
  • 可預處理Data,處理空Data
  • 序列化數據爲Data類型
  • 擴展DataRequest與DownloadRequest添加responseData序列化方法
public final class DataResponseSerializer: ResponseSerializer {
    /// 實現ResponseSerializer協議的三個屬性
    public let dataPreprocessor: DataPreprocessor
    public let emptyResponseCodes: Set<Int>
    public let emptyRequestMethods: Set<HTTPMethod>

    public init(dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) {
        self.dataPreprocessor = dataPreprocessor
        self.emptyResponseCodes = emptyResponseCodes
        self.emptyRequestMethods = emptyRequestMethods
    }

    public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Data {
        guard error == nil else { throw error! }

        guard var data = data, !data.isEmpty else {
            // 若是data空, (包括nil, 空Data), 調用協議擴展的檢測是否容許空Data的方法
            guard emptyResponseAllowed(forRequest: request, response: response) else {
                // 不容許空Data就拋出錯誤
                throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
            }
            // 容許空Data, 就返回一個空的Data對象
            return Data()
        }
        // 預處理Data
        data = try dataPreprocessor.preprocess(data)
        // 返回類型爲Data
        return data
    }
}

extension DataRequest {
    ///擴展DataRequest, 添加解析響應回調
    @discardableResult
    public func responseData(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDataResponse<Data>) -> Void) -> Self {
        // 調用上面通用的使用序列化協議對象解析的方法
        response(queue: queue,
                 responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                            emptyResponseCodes: emptyResponseCodes,
                                                            emptyRequestMethods: emptyRequestMethods),
                 completionHandler: completionHandler)
    }
}

extension DownloadRequest {
    /// 同上
    @discardableResult
    public func responseData(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDownloadResponse<Data>) -> Void) -> Self {
        response(queue: queue,
                 responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                            emptyResponseCodes: emptyResponseCodes,
                                                            emptyRequestMethods: emptyRequestMethods),
                 completionHandler: completionHandler)
    }
}
複製代碼
5.String格式預處理器
  • 定義StringResponseSerializer序列化器實現ResponseSerializer協議並修飾爲final,不可繼承
  • 可指定String編碼格式,默認爲.isoLatin1。優先級:手動設置 > 服務器返回 > 默認的isoLatin1
  • 可預處理Data,處理空Data
  • 序列化數據爲Data類型
  • 擴展DataRequest與DownloadRequest添加responseString序列化方法
public final class StringResponseSerializer: ResponseSerializer {
    public let dataPreprocessor: DataPreprocessor
    /// string編碼格式
    public let encoding: String.Encoding?
    public let emptyResponseCodes: Set<Int>
    public let emptyRequestMethods: Set<HTTPMethod>

    public init(dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, encoding: String.Encoding? = nil, emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) {
        self.dataPreprocessor = dataPreprocessor
        self.encoding = encoding
        self.emptyResponseCodes = emptyResponseCodes
        self.emptyRequestMethods = emptyRequestMethods
    }

    public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> String {
        guard error == nil else { throw error! }

        //先檢測下空data狀況, 拋出異常或者是返回空字符串
        guard var data = data, !data.isEmpty else {
            guard emptyResponseAllowed(forRequest: request, response: response) else {
                throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
            }

            return ""
        }
        
        //預處理Data
        data = try dataPreprocessor.preprocess(data)

        //string編碼格式
        var convertedEncoding = encoding
        
        if let encodingName = response?.textEncodingName, convertedEncoding == nil {
            // 未指定編碼格式, 且服務器有返回編碼格式
            convertedEncoding = String.Encoding(ianaCharsetName: encodingName)
        }
        // 實際編碼格式: 手動設置 > 服務器返回 > 默認的isoLatin1
        let actualEncoding = convertedEncoding ?? .isoLatin1

        // 編碼, 編碼失敗拋出錯誤
        guard let string = String(data: data, encoding: actualEncoding) else {
            throw AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding))
        }

        return string
    }
}

extension DataRequest {
    /// 添加解析方法
    @discardableResult
    public func responseString(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, encoding: String.Encoding? = nil, emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDataResponse<String>) -> Void) -> Self {
        response(queue: queue,
                 responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                              encoding: encoding,
                                                              emptyResponseCodes: emptyResponseCodes,
                                                              emptyRequestMethods: emptyRequestMethods),
                 completionHandler: completionHandler)
    }
}

extension DownloadRequest {
    /// 同上
    @discardableResult
    public func responseString(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, encoding: String.Encoding? = nil, emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDownloadResponse<String>) -> Void) -> Self {
        response(queue: queue,
                 responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                              encoding: encoding,
                                                              emptyResponseCodes: emptyResponseCodes,
                                                              emptyRequestMethods: emptyRequestMethods),
                 completionHandler: completionHandler)
    }
}
複製代碼
6.JOSN格式序列化器(兩種)
  • 定義JSONResponseSerializer序列化器實現ResponseSerializer協議並修飾爲final,不可繼承
  • 可指定JSON讀取格式,默認爲.allowFragments
  • 使用系統的JSONSerialization解析Data
  • 可預處理Data,處理空Data
  • 序列化數據爲Data類型
  • 擴展DataRequest與DownloadRequest添加responseJSON序列化方法
public final class JSONResponseSerializer: ResponseSerializer {
    public let dataPreprocessor: DataPreprocessor
    public let emptyResponseCodes: Set<Int>
    public let emptyRequestMethods: Set<HTTPMethod>
    /// JSON讀取格式
    public let options: JSONSerialization.ReadingOptions

    public init(dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set<Int> = JSONResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = JSONResponseSerializer.defaultEmptyRequestMethods, options: JSONSerialization.ReadingOptions = .allowFragments) {
        self.dataPreprocessor = dataPreprocessor
        self.emptyResponseCodes = emptyResponseCodes
        self.emptyRequestMethods = emptyRequestMethods
        self.options = options
    }

    // 實現協議的解析方法
    public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Any {
        guard error == nil else { throw error! }

        // 判斷處理空Data
        guard var data = data, !data.isEmpty else {
            guard emptyResponseAllowed(forRequest: request, response: response) else {
                throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
            }

            return NSNull()
        }
        // 預處理
        data = try dataPreprocessor.preprocess(data)

        do {
            // 序列化JSON
            return try JSONSerialization.jsonObject(with: data, options: options)
        } catch {
            // 捕捉拋出錯誤
            throw AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error))
        }
    }
}

extension DataRequest {
    /// 添加響應方法
    @discardableResult
    public func responseJSON(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set<Int> = JSONResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = JSONResponseSerializer.defaultEmptyRequestMethods, options: JSONSerialization.ReadingOptions = .allowFragments, completionHandler: @escaping (AFDataResponse<Any>) -> Void) -> Self {
        response(queue: queue,
                 responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                            emptyResponseCodes: emptyResponseCodes,
                                                            emptyRequestMethods: emptyRequestMethods,
                                                            options: options),
                 completionHandler: completionHandler)
    }
}

extension DownloadRequest {
    /// 同上
    @discardableResult
    public func responseJSON(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set<Int> = JSONResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = JSONResponseSerializer.defaultEmptyRequestMethods, options: JSONSerialization.ReadingOptions = .allowFragments, completionHandler: @escaping (AFDownloadResponse<Any>) -> Void) -> Self {
        response(queue: queue,
                 responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                            emptyResponseCodes: emptyResponseCodes,
                                                            emptyRequestMethods: emptyRequestMethods,
                                                            options: options),
                 completionHandler: completionHandler)
    }
}
複製代碼
7.Decodable格式序列化器(兩種)

由於Decodable是協議類型,因此稍微有一丟丟複雜

  • 聲明瞭空數據協議類型EmptyResponse,用一個靜態方法emptyValue()來返回空的數據類型
  • 聲明瞭Empty結構體,實現了Codable與EmptyResponse協議,能夠用來當作默認的EmptyResponse使用(實際上並無用到,惟一的用處是在使用Encodable協議類型參數初始化StreamRequest時,做爲默認空參數使用)
  • 聲明瞭DataDecoder解碼器協議,用來把Data類型的響應數據解碼爲Decoable協議類型,由於解碼方法與系統的默認的JSONDecoder以及PropertyListDecoder的解碼同名,所以只用聲明下這兩個系統解碼器實現DataDecoder協議便可直接拿來用
  • 定義DecodableResponseSerializer序列化器實現ResponseSerializer協議並修飾爲final,不可繼承,而且聲明瞭泛型T實現Decodable協議做爲序列化結果類型
  • 持有DataDecoder解碼器協議屬性,用來解碼Data,默認爲JSONDecoder
  • 可預處理Data,處理空Data
  • 序列化數據爲Data類型
  • 擴展DataRequest與DownloadRequest添加responseDecodable序列化方法
public final class DecodableResponseSerializer<T: Decodable>: ResponseSerializer {
    public let dataPreprocessor: DataPreprocessor
    /// 用來解碼的解碼器, 默認爲系統JSONDecoder解碼器
    public let decoder: DataDecoder
    public let emptyResponseCodes: Set<Int>
    public let emptyRequestMethods: Set<HTTPMethod>

    public init(dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, decoder: DataDecoder = JSONDecoder(), emptyResponseCodes: Set<Int> = DecodableResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer.defaultEmptyRequestMethods) {
        self.dataPreprocessor = dataPreprocessor
        self.decoder = decoder
        self.emptyResponseCodes = emptyResponseCodes
        self.emptyRequestMethods = emptyRequestMethods
    }
    /// 實現ResponseSerializer協議的解析方法
    public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T {
        guard error == nil else { throw error! }

        // 處理空數據
        guard var data = data, !data.isEmpty else {
            guard emptyResponseAllowed(forRequest: request, response: response) else {
                throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
            }
            //從解析結果類型T, 獲取空數據常量
            guard let emptyResponseType = T.self as? EmptyResponse.Type, let emptyValue = emptyResponseType.emptyValue() as? T else {
                // 解析結果類型T若不遵循EmptyResponse類型就拋出錯誤
                throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)"))
            }
            // 返回默認空數據
            return emptyValue
        }
        //預處理Data
        data = try dataPreprocessor.preprocess(data)

        do {
            // 解碼Data
            return try decoder.decode(T.self, from: data)
        } catch {
            // 捕捉拋出異常
            throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error))
        }
    }
}

extension DataRequest {
    /// 追加
    @discardableResult
    public func responseDecodable<T: Decodable>(of type: T.Type = T.self, queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor, decoder: DataDecoder = JSONDecoder(), emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods, completionHandler: @escaping (AFDataResponse<T>) -> Void) -> Self {
        response(queue: queue,
                 responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                                 decoder: decoder,
                                                                 emptyResponseCodes: emptyResponseCodes,
                                                                 emptyRequestMethods: emptyRequestMethods),
                 completionHandler: completionHandler)
    }
}

extension DownloadRequest {
    /// 同上
    @discardableResult
    public func responseDecodable<T: Decodable>(of type: T.Type = T.self, queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor, decoder: DataDecoder = JSONDecoder(), emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes, emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods, completionHandler: @escaping (AFDownloadResponse<T>) -> Void) -> Self {
        response(queue: queue,
                 responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                                 decoder: decoder,
                                                                 emptyResponseCodes: emptyResponseCodes,
                                                                 emptyRequestMethods: emptyRequestMethods),
                 completionHandler: completionHandler)
    }
}
複製代碼

DataStreamRequest請求響應數據的序列化

上面的相應數據來自於DataRequest與DownloadRequest,請求均爲一次性請求完成,而DataStreamRequest的響應比較特殊,會持續收到Data數據,所以在序列化DataStreamResponse時,只須要對每一個收到的Data數據進行解析便可,相對上面兩種簡單一點

序列化協議

同上面同樣,也是採用協議來解耦。序列化協議也很簡單,含有一個泛型的結果數據類型,以及一個序列化方法,該方法只用解析Data,不用解析Request與Response。

public protocol DataStreamSerializer {
    /// 序列化結果泛型
    associatedtype SerializedObject
    /// 序列化方法
    func serialize(_ data: Data) throws -> SerializedObject
}
複製代碼

定義內置序列化器與添加序列化方法

Alamofire爲StreamRequest定義了3中內置序列化器,添加了4中序列化方法。

  • 相對上面Data與Download請求,少了fileurl以及JSON格式的序列化器
  • 序列化器是放在一塊兒定義,序列化方法也是一次性添加,不一樣於Data與Download請求序列化的定義一個序列化器緊跟着添加序列化方法。
  • 由於不須要處理Request與Response,且沒有重試邏輯,所以解析器邏輯其實很是簡單
1.DecodableStreamSerializer可解碼協議類型對象序列化器
  • 將Data序列化爲符合Decodable協議的泛型對象
  • 持有DataDecoder泛型解碼器對象,默認解碼器爲系統JSONDecoder
  • 持有預處理器,能夠預處理Data
  • 解析邏輯相似上面
public struct DecodableStreamSerializer<T: Decodable>: DataStreamSerializer {
    /// 解析Data的解析器, 默認爲系統的JSONDecoder
    public let decoder: DataDecoder
    /// Data預處理器
    public let dataPreprocessor: DataPreprocessor

    public init(decoder: DataDecoder = JSONDecoder(), dataPreprocessor: DataPreprocessor = PassthroughPreprocessor()) {
        self.decoder = decoder
        self.dataPreprocessor = dataPreprocessor
    }
    // 解析
    public func serialize(_ data: Data) throws -> T {
        // 預處理
        let processedData = try dataPreprocessor.preprocess(data)
        do {
            // 解析Data
            return try decoder.decode(T.self, from: processedData)
        } catch {
            // 捕捉異常並拋出
            throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error))
        }
    }
}
複製代碼
2.PassthroughStreamSerializer透傳序列化器
  • 對data不作任何處理,僅僅是透傳
  • 做爲默認序列化器使用
public struct PassthroughStreamSerializer: DataStreamSerializer {
    public func serialize(_ data: Data) throws -> Data { data }
}
複製代碼
3.StringStreamSerializer字符串序列化器
  • 把Data使用UTF8解碼爲string
public struct StringStreamSerializer: DataStreamSerializer {
    public func serialize(_ data: Data) throws -> String {
        String(decoding: data, as: UTF8.self)
    }
}
複製代碼

爲DataStreamRequest添加序列化方法

每一個添加個Request的response***方法,本質上都是爲Request添加了一個閉包,每次收到數據後,Request都會調用該閉包處理數據,不一樣於上面Data於Download請求的閉包只會調用一次,DataStream添加的閉包類型被定義爲別名Handler,該閉包的入參爲Stream結構體,每一個Stream表明數據流中的一個數據單元,能夠表示數據事件,或者完成事件。隨着請求不斷執行,Handler會被不斷調用,所有解析完成後,會對該Handler執行完成處理

1.添加以Data格式處理響應的方法
  • 收到數據並無進行序列化處理,只是簡單的執行後續流程
  • 會爲Request添加一個處理流的回調,保存在Mutable.streams數組中
  • 把該回調又封裝了一個完成回調,保存在Mutable.enqueuedCompletionEvents數組中,用來在所有數據解析完成以後,發送完成數據單元。
  • 爲DataStreamRequest添加responseStream方法
@discardableResult
    public func responseStream(on queue: DispatchQueue = .main, stream: @escaping Handler<Data, Never>) -> Self {
        // 建立解析回調paser, 而後追加到Request的解析回調隊列裏
        let parser = { [unowned self] (data: Data) in
            queue.async {
                self.capturingError {
                    // 執行Handle, 捕捉異常
                    try stream(.init(event: .stream(.success(data)), token: .init(self)))
                }
                // 更新狀態,並檢測是否須要完成
                self.updateAndCompleteIfPossible()
            }
        }
        // 追加解析回調
        $streamMutableState.write { $0.streams.append(parser) }
        // 把完成回調追加給Handle
        appendStreamCompletion(on: queue, stream: stream)

        return self
    }
複製代碼
2.添加以使用DataStreamSerializer泛型序列化器處理方法

總體邏輯與上面普通處理相似,多了數據解析的步驟,以及通知監聽器

  • 解析後的數據爲DataStreamSerializer泛型序列化中的數據類型
  • 是最基本的解析方法,下面兩個解析方法都會使用對應的序列化器實例派發過來調用
  • 解析處理須要在serializationQueue隊列執行
  • 解析完成後會通知eventMonitor事件監聽器
  • 解析失敗會根據automaticallyCancelOnStreamError屬性來決定是否取消請求
  • 爲DataStreamRequest追加responseStream序列化方法
@discardableResult
    public func responseStream<Serializer: DataStreamSerializer>(using serializer: Serializer, on queue: DispatchQueue = .main, stream: @escaping Handler<Serializer.SerializedObject, AFError>) -> Self {
        // 建立parser回調
        let parser = { [unowned self] (data: Data) in
            self.serializationQueue.async {
                // 在解析隊列開始解析任務
                let result = Result { try serializer.serialize(data) }
                    .mapError { $0.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: $0))) }
                // 解析完成
                self.underlyingQueue.async {
                    self.eventMonitor?.request(self, didParseStream: result)
                    
                    // 若是解析失敗, 且設置了失敗取消的話, 取消請求
                    if result.isFailure, self.automaticallyCancelOnStreamError {
                        self.cancel()
                    }

                    queue.async {
                        self.capturingError {
                            try stream(.init(event: .stream(result), token: .init(self)))
                        }

                        //更新狀態,並檢測是否須要完成
                        self.updateAndCompleteIfPossible()
                    }
                }
            }
        }
        // 添加到回調數組
        $streamMutableState.write { $0.streams.append(parser) }
        // 追加流完成數據
        appendStreamCompletion(on: queue, stream: stream)

        return self
    }
複製代碼
3.添加解析爲String的序列化方法
  • 把Data使用UTF8解碼爲String
  • stream處理回調入參類型的錯誤格式是Never而不是AFError,默認解碼爲String時,不會失敗
  • 理論上應該使用上面定義的StringStreamSerializer序列化器,可是實際上,由於解析操做比較簡單,且stream處理回調入參類型不一樣,所以並無使用StringStreamSerializer來解析,而是直接用相似處理data的寫法
  • 爲DataStreamRequest追加responseStreamString方法
@discardableResult
    public func responseStreamString(on queue: DispatchQueue = .main, stream: @escaping Handler<String, Never>) -> Self {
        let parser = { [unowned self] (data: Data) in
            self.serializationQueue.async {
                // Start work on serialization queue.
                let string = String(decoding: data, as: UTF8.self)
                // End work on serialization queue.
                self.underlyingQueue.async {
                    self.eventMonitor?.request(self, didParseStream: .success(string))

                    queue.async {
                        self.capturingError {
                            try stream(.init(event: .stream(.success(string)), token: .init(self)))
                        }

                        //更新狀態,並檢測是否須要完成
                        self.updateAndCompleteIfPossible()
                    }
                }
            }
        }

        $streamMutableState.write { $0.streams.append(parser) }
        appendStreamCompletion(on: queue, stream: stream)

        return self
    }
複製代碼
更新狀態以及檢測是否解析完成的方法

當每一個Stream請求收到Data後,開始準備解析數據,會把對應DataStreamRequest的numberOfExecutingStreams加上所持有的Stream流回調個數,表示這些回調所有進入解析狀態,而後每一個流解析完成後,都會把numberOfExecutingStreams減1,並在解析完成後檢測是否減爲0,若numberOfExecutingStreams == 0表示全部回調都已解析完該data,DataStreamRequest就對保存在enqueuedCompletionEvents中的完成回調逐個調用,調用完成後所有移除,而在解析期間若是有添加新的解析回調,解析回調用的完成回調就會先存入enqueuedCompletionEvents中,等待所有流回調處理完成後逐個執行。

private func updateAndCompleteIfPossible() {
        $streamMutableState.write { state in
            //處理中的流回調減1
            state.numberOfExecutingStreams -= 1

            guard state.numberOfExecutingStreams == 0, !state.enqueuedCompletionEvents.isEmpty else {
                // 若還有流回調在處理,或者沒有完成處理回調,就直接返回
                return
            }
            // 所有流回調均已處理完畢,開始處理完成回調
            let completionEvents = state.enqueuedCompletionEvents
            // 逐個調用
            self.underlyingQueue.async { completionEvents.forEach { $0() } }
            // 清空保存的完成回調
            state.enqueuedCompletionEvents.removeAll()
        }
    }
複製代碼
3+. 使用StringStreamSerializer序列化器處理響應

既然已經定義了StringStreamSerializer字符串序列化器,那麼String解析處理其實能夠直接使用這個序列化器處理,並且邏輯更加簡單:

//爲DataStreamRequest添加一個responseStreamString2解析方法
    @discardableResult
    public func responseStreamString2(on queue: DispatchQueue = .main, stream: @escaping Handler<String, AFError>) -> Self {
        responseStream(using: StringStreamSerializer(), on: queue, stream: stream)
    }
複製代碼

雖然stream處理的Handle閉包的錯誤參數爲AFError,可是該錯誤實際上是在解析出錯時拋出的,而StringStreamSerializer解析方法根本不會拋出錯誤,也就意味着Handler<String, AFError> 等同於Handler<String, Never>,絕對不會出錯。

4. 使用DecodableStreamSerializer泛型解析器處理響應
  • 使用DecodableStreamSerializer泛型序列化器解析Data
  • 爲DataStreamRequest添加responseStreamDecodable解析方法
@discardableResult
    public func responseStreamDecodable<T: Decodable>(of type: T.Type = T.self, on queue: DispatchQueue = .main, using decoder: DataDecoder = JSONDecoder(), preprocessor: DataPreprocessor = PassthroughPreprocessor(), stream: @escaping Handler<T, AFError>) -> Self {
        responseStream(using: DecodableStreamSerializer<T>(decoder: decoder, dataPreprocessor: preprocessor),
                       stream: stream)
    }
複製代碼

總結

爲Request擴展添加response***響應方法,本質上時給Request添加了兩個核心閉包:

  • 解析閉包:用來解析數據(Data與Download請求只調用一次,Stream請求會調用屢次)
  • 完成閉包:用來處理解析完成邏輯(Data與Download是在解析閉包中添加,Stream是在添加解析閉包後添加,並且完成比寶會引用解析閉包)

涉及到各類閉包嵌套,並且加上swift的尾隨閉包寫法,看起來很難,下一節詳細總結下。。。

以上純屬我的理解,不免有誤,如發現有錯誤的地方,歡迎評論指出,將第一時間修改,很是感謝~

相關文章
相關標籤/搜索