響應與解析由兩個核心模塊組成:swift
分爲兩種響應:api
使用場景:
在使用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> 複製代碼
封裝響應頭, 響應數據, 以及響應對應的請求, 同時定義泛型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
}
複製代碼
由於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))
}
}
複製代碼
跟上面的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描述,以及四個類型變換的方法,基本上一毛同樣,就再也不贅述
在兩個Response實現DebugString協議時,使用了兩個輔助類型:
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()) """
}
}
複製代碼
extension String {
fileprivate func indentingNewlines(by spaceCount: Int = 4) -> String {
let spaces = String(repeating: " ", count: spaceCount)
return replacingOccurrences(of: "\n", with: "\n\(spaces)")
}
}
複製代碼
Alamofire的一個核心爲Session+Request及其子類,管理Session,建立URLRequest,發送URLSessionTask,管理Request與Task的關聯,處理請求中的各個事件以及錯誤處理。當請求完成時,須要對響應數據進行解析,默承認以解析爲JSON,String,Decodable等格式,也能夠本身實現接口把響應數據解析爲其餘接口。所以另一個核心就是ResponseSerialization響應解析。整個網絡請求的流程中,核心就兩點:1.發送請求,2.處理請求
很是簡單,就一個泛型序列化數據類型,一個簡單的序列化方法,分爲Data與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
}
複製代碼
將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 }
}
複製代碼
協議很簡單,就一個方法,入參出參均爲Data,實現對象能夠對入參Data進行預處理,執行增刪改查操做後,返回處理後的Data,再繼續進行解析,方法容許拋出異常,拋出異常時,上層會catch異常並封裝成AFError返回
/// 用來預處理Data的協議, Alamofire有兩個默認實現
public protocol DataPreprocessor {
/// 出入參均爲Data, 容許拋出異常, 拋出異常時封裝爲AFError返回
func preprocess(_ data: Data) throws -> Data
}
複製代碼
Alamofire實現了兩個預處理器struct,權限爲public,能夠直接使用
沒作任何處理,只是把Data原樣傳遞回去,用來在實現ResponseSerializer協議時,做爲默認的預處理器使用
/// 默認透傳處理器, 不作任何處理
public struct PassthroughPreprocessor: DataPreprocessor {
public init() {}
public func preprocess(_ data: Data) throws -> Data { data }
}
複製代碼
去掉數據前綴的)]}',\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添加了三個屬性的默認實現,而且添加了三個快捷方法來判斷請求與相應是否容許響應數據爲空
/// 擴展協議, 給三個屬性添加默認實現
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)
}
}
複製代碼
當某個序列化器,即實現了Data序列化接口以及Download序列化接口,那麼能夠把Download序列化的行爲看作:
所以就能夠對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
}
}
}
複製代碼
由於原數據就是Data或者fileurl類型,所以就沒有再專門定義序列化器,而是直接對兩種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
}
}
複製代碼
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
}
}
複製代碼
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)
}
}
複製代碼
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)
}
}
複製代碼
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)
}
}
複製代碼
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)
}
}
複製代碼
由於Decodable是協議類型,因此稍微有一丟丟複雜
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)
}
}
複製代碼
上面的相應數據來自於DataRequest與DownloadRequest,請求均爲一次性請求完成,而DataStreamRequest的響應比較特殊,會持續收到Data數據,所以在序列化DataStreamResponse時,只須要對每一個收到的Data數據進行解析便可,相對上面兩種簡單一點
同上面同樣,也是採用協議來解耦。序列化協議也很簡單,含有一個泛型的結果數據類型,以及一個序列化方法,該方法只用解析Data,不用解析Request與Response。
public protocol DataStreamSerializer {
/// 序列化結果泛型
associatedtype SerializedObject
/// 序列化方法
func serialize(_ data: Data) throws -> SerializedObject
}
複製代碼
Alamofire爲StreamRequest定義了3中內置序列化器,添加了4中序列化方法。
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))
}
}
}
複製代碼
public struct PassthroughStreamSerializer: DataStreamSerializer {
public func serialize(_ data: Data) throws -> Data { data }
}
複製代碼
public struct StringStreamSerializer: DataStreamSerializer {
public func serialize(_ data: Data) throws -> String {
String(decoding: data, as: UTF8.self)
}
}
複製代碼
每一個添加個Request的response***方法,本質上都是爲Request添加了一個閉包,每次收到數據後,Request都會調用該閉包處理數據,不一樣於上面Data於Download請求的閉包只會調用一次,DataStream添加的閉包類型被定義爲別名Handler,該閉包的入參爲Stream結構體,每一個Stream表明數據流中的一個數據單元,能夠表示數據事件,或者完成事件。隨着請求不斷執行,Handler會被不斷調用,所有解析完成後,會對該Handler執行完成處理
@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
}
複製代碼
總體邏輯與上面普通處理相似,多了數據解析的步驟,以及通知監聽器
@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
}
複製代碼
@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()
}
}
複製代碼
既然已經定義了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>,絕對不會出錯。
@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添加了兩個核心閉包:
涉及到各類閉包嵌套,並且加上swift的尾隨閉包寫法,看起來很難,下一節詳細總結下。。。
以上純屬我的理解,不免有誤,如發現有錯誤的地方,歡迎評論指出,將第一時間修改,很是感謝~