本篇主要帶來(lái)Alamofire中Response的解讀
前言
在每篇文章的前言部分,我都會(huì)把我認(rèn)為的本篇最重要的內(nèi)容提前講一下。我更想同大家分享這些頂級(jí)框架在設(shè)計(jì)和編碼層次究竟有哪些過(guò)人的地方?當(dāng)然,這些理解也都是基于我自己的理解。難免具有局限性。
當(dāng)我們?cè)O(shè)計(jì)完一個(gè)Request的時(shí)候,我們肯定要處理服務(wù)器返回的響應(yīng)數(shù)據(jù)。在Alamofire源碼解讀系列(一)之概述和使用中,我們已經(jīng)講過(guò),Alamofire中把Request分為了4類(lèi):
- DataRequest
- DownloadRequest
- UploadRequest
- StreamRequest
Alamofire中Request可以使用鏈?zhǔn)皆L(fǎng)問(wèn):
Alamofire.request("https://httpbin.org/get")
.responseString { response in
print("Response String: \(response.result.value)")
}
.responseJSON { response in
print("Response JSON: \(response.result.value)")
}
能實(shí)現(xiàn)鏈?zhǔn)皆L(fǎng)問(wèn)的原理就是每個(gè)函數(shù)的返回值都是Self。那么在上邊的代碼中,雖然response的名字都一樣,但并不是同一類(lèi)型。
因?yàn)橛?中不同的Request類(lèi)型,StreamRequest我們先不提,對(duì)于UploadRequest來(lái)說(shuō),服務(wù)器響應(yīng)的數(shù)據(jù)比較簡(jiǎn)單,就響應(yīng)一個(gè)結(jié)果就行,因此不需要對(duì)它的Response專(zhuān)門(mén)進(jìn)行封裝。因此,Alamofire設(shè)計(jì)了2中與之相對(duì)應(yīng)的Response類(lèi)型,他們分別是:
- DefaultDataResponse / DataResponse
- DefaultDownloadResponse / DataResponse
那么,如果我們使用下邊的代碼獲取響應(yīng)數(shù)據(jù):
// Response Handler - Unserialized Response
func response(
queue: DispatchQueue?,
completionHandler: @escaping (DefaultDataResponse) -> Void)
-> Self
獲取的是沒(méi)有經(jīng)過(guò)序列化后的數(shù)據(jù),如果使用了沒(méi)有序列化的response方法,返回的就是帶有Default開(kāi)頭的響應(yīng)者,比如DefaultDataResponse,DefaultDownloadResponse。如果使用了序列化的response方法,返回的就是DataResponse或者DataResponse。
這說(shuō)明了什么? 在程序的設(shè)計(jì)層面上,這種前后呼應(yīng)的手法能夠讓代碼更好理解。就像在項(xiàng)目中不能把所有的參數(shù)都放到一個(gè)模型中一樣。
拿DefaultDataResponse / DataResponse來(lái)舉例,DataResponse基本上只比DefaultDataResponse多了一個(gè)系列化后的數(shù)據(jù)屬性。
還有一點(diǎn)要提一下,先假設(shè)每個(gè)Request都可以被序列化為JSON,或者String。這些都屬于序列化Response的范圍。序列化成功后,保存數(shù)據(jù)的容器應(yīng)該有一個(gè)類(lèi)型,而這個(gè)類(lèi)型又是可以變化的,在本篇文章下邊的內(nèi)容中會(huì)指出泛型的使用方法。
DefaultDataResponse
DefaultDataResponse用于存儲(chǔ)data或upload請(qǐng)求情況下的所有無(wú)序列化的數(shù)據(jù)。那么在Alamofire中對(duì)于服務(wù)器的響應(yīng)主要關(guān)心的數(shù)據(jù)有下邊幾個(gè):
-
request: URLRequest?表示該響應(yīng)來(lái)源于那個(gè)請(qǐng)求 -
response: HTTPURLResponse?服務(wù)器返回的響應(yīng) -
data: Data?響應(yīng)數(shù)據(jù) -
error: Error?在請(qǐng)求中可能發(fā)生的錯(cuò)誤 -
timeline: Timeline請(qǐng)求的時(shí)間線(xiàn)封裝,這個(gè)會(huì)在后續(xù)的文章中解釋 -
_metrics: AnyObject?包含了請(qǐng)求和響應(yīng)的統(tǒng)計(jì)信息
代碼如下:
/// The URL request sent to the server.
public let request: URLRequest?
/// The server's response to the URL request.
public let response: HTTPURLResponse?
/// The data returned by the server.
public let data: Data?
/// The error encountered while executing or validating the request.
public let error: Error?
/// The timeline of the complete lifecycle of the request.
public let timeline: Timeline
var _metrics: AnyObject?
一般來(lái)說(shuō),在swift中,如果只是為了保存數(shù)據(jù),那么應(yīng)該把這個(gè)類(lèi)設(shè)計(jì)成struct。struct是 值傳遞,因此對(duì)數(shù)據(jù)的操作更安全。除了定義需要保存的數(shù)據(jù)屬性后,必須設(shè)計(jì)一個(gè)符合要求的構(gòu)造函數(shù)。
/// Creates a `DefaultDataResponse` instance from the specified parameters.
///
/// - Parameters:
/// - request: The URL request sent to the server.
/// - response: The server's response to the URL request.
/// - data: The data returned by the server.
/// - error: The error encountered while executing or validating the request.
/// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
/// - metrics: The task metrics containing the request / response statistics. `nil` by default.
public init(
request: URLRequest?,
response: HTTPURLResponse?,
data: Data?,
error: Error?,
timeline: Timeline = Timeline(),
metrics: AnyObject? = nil)
{
self.request = request
self.response = response
self.data = data
self.error = error
self.timeline = timeline
}
DataResponse<Value>
DataResponse<Value>比上邊的DefaultDataResponse多了一個(gè)result屬性,該屬性存儲(chǔ)了序列化后的數(shù)據(jù)。它的類(lèi)型是 Result<Value>,關(guān)于Result的詳情內(nèi)容,請(qǐng)看這篇文章Alamofire源碼解讀系列(五)之結(jié)果封裝(Result)
/// Used to store all data associated with a serialized response of a data or upload request.
public struct DataResponse<Value> {
/// The URL request sent to the server.
public let request: URLRequest?
/// The server's response to the URL request.
public let response: HTTPURLResponse?
/// The data returned by the server.
public let data: Data?
/// The result of response serialization.
public let result: Result<Value>
/// The timeline of the complete lifecycle of the request.
public let timeline: Timeline
/// Returns the associated value of the result if it is a success, `nil` otherwise.
public var value: Value? { return result.value }
/// Returns the associated error value if the result if it is a failure, `nil` otherwise.
public var error: Error? { return result.error }
var _metrics: AnyObject?
/// Creates a `DataResponse` instance with the specified parameters derived from response serialization.
///
/// - parameter request: The URL request sent to the server.
/// - parameter response: The server's response to the URL request.
/// - parameter data: The data returned by the server.
/// - parameter result: The result of response serialization.
/// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
///
/// - returns: The new `DataResponse` instance.
public init(
request: URLRequest?,
response: HTTPURLResponse?,
data: Data?,
result: Result<Value>,
timeline: Timeline = Timeline())
{
self.request = request
self.response = response
self.data = data
self.result = result
self.timeline = timeline
}
}
DataResponse: CustomStringConvertible, CustomDebugStringConvertible
DataResponse實(shí)現(xiàn)了CustomStringConvertible和CustomDebugStringConvertible協(xié)議,因此可以自定義DataResponse的打印信息。
我們也可以給我們模型實(shí)現(xiàn)這兩個(gè)協(xié)議,在代碼調(diào)試的時(shí)候,打印出詳細(xì)的信息,比打斷點(diǎn)來(lái)查看效率更高。
extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible {
/// The textual representation used when written to an output stream, which includes whether the result was a
/// success or failure.
public var description: String {
return result.debugDescription
}
/// The debug textual representation used when written to an output stream, which includes the URL request, the URL
/// response, the server data, the response serialization result and the timeline.
public var debugDescription: String {
var output: [String] = []
output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")
output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
output.append("[Data]: \(data?.count ?? 0) bytes")
output.append("[Result]: \(result.debugDescription)")
output.append("[Timeline]: \(timeline.debugDescription)")
return output.joined(separator: "\n")
}
}
DefaultDownloadResponse
DefaultDownloadResponse保存的是下載任務(wù)的數(shù)據(jù)。有3個(gè)屬性需要做一下介紹:
-
temporaryURL: URL?現(xiàn)在成功后,數(shù)據(jù)會(huì)被保存在這個(gè)臨時(shí)URL中 -
destinationURL: URL?目標(biāo)URL,如果設(shè)置了該屬性,那么文件會(huì)復(fù)制到該URL中 -
resumeData: Data?表示可恢復(fù)的數(shù)據(jù),對(duì)于下載任務(wù),如果因?yàn)槟撤N原因下載中斷了,或失敗了,可以使用該數(shù)據(jù)恢復(fù)之前的下載
其他的內(nèi)容跟上邊介紹的內(nèi)容沒(méi)什么特別的地方,就簡(jiǎn)單的把代碼弄上來(lái)了:
/// Used to store all data associated with an non-serialized response of a download request.
public struct DefaultDownloadResponse {
/// The URL request sent to the server.
public let request: URLRequest?
/// The server's response to the URL request.
public let response: HTTPURLResponse?
/// The temporary destination URL of the data returned from the server.
public let temporaryURL: URL?
/// The final destination URL of the data returned from the server if it was moved.
public let destinationURL: URL?
/// The resume data generated if the request was cancelled.
public let resumeData: Data?
/// The error encountered while executing or validating the request.
public let error: Error?
/// The timeline of the complete lifecycle of the request.
public let timeline: Timeline
var _metrics: AnyObject?
/// Creates a `DefaultDownloadResponse` instance from the specified parameters.
///
/// - Parameters:
/// - request: The URL request sent to the server.
/// - response: The server's response to the URL request.
/// - temporaryURL: The temporary destination URL of the data returned from the server.
/// - destinationURL: The final destination URL of the data returned from the server if it was moved.
/// - resumeData: The resume data generated if the request was cancelled.
/// - error: The error encountered while executing or validating the request.
/// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
/// - metrics: The task metrics containing the request / response statistics. `nil` by default.
public init(
request: URLRequest?,
response: HTTPURLResponse?,
temporaryURL: URL?,
destinationURL: URL?,
resumeData: Data?,
error: Error?,
timeline: Timeline = Timeline(),
metrics: AnyObject? = nil)
{
self.request = request
self.response = response
self.temporaryURL = temporaryURL
self.destinationURL = destinationURL
self.resumeData = resumeData
self.error = error
self.timeline = timeline
}
}
DownloadResponse
這個(gè)也沒(méi)什么好說(shuō)的,直接上代碼:
/// Used to store all data associated with a serialized response of a download request.
public struct DownloadResponse<Value> {
/// The URL request sent to the server.
public let request: URLRequest?
/// The server's response to the URL request.
public let response: HTTPURLResponse?
/// The temporary destination URL of the data returned from the server.
public let temporaryURL: URL?
/// The final destination URL of the data returned from the server if it was moved.
public let destinationURL: URL?
/// The resume data generated if the request was cancelled.
public let resumeData: Data?
/// The result of response serialization.
public let result: Result<Value>
/// The timeline of the complete lifecycle of the request.
public let timeline: Timeline
/// Returns the associated value of the result if it is a success, `nil` otherwise.
public var value: Value? { return result.value }
/// Returns the associated error value if the result if it is a failure, `nil` otherwise.
public var error: Error? { return result.error }
var _metrics: AnyObject?
/// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization.
///
/// - parameter request: The URL request sent to the server.
/// - parameter response: The server's response to the URL request.
/// - parameter temporaryURL: The temporary destination URL of the data returned from the server.
/// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved.
/// - parameter resumeData: The resume data generated if the request was cancelled.
/// - parameter result: The result of response serialization.
/// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
///
/// - returns: The new `DownloadResponse` instance.
public init(
request: URLRequest?,
response: HTTPURLResponse?,
temporaryURL: URL?,
destinationURL: URL?,
resumeData: Data?,
result: Result<Value>,
timeline: Timeline = Timeline())
{
self.request = request
self.response = response
self.temporaryURL = temporaryURL
self.destinationURL = destinationURL
self.resumeData = resumeData
self.result = result
self.timeline = timeline
}
}
DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible
extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible {
/// The textual representation used when written to an output stream, which includes whether the result was a
/// success or failure.
public var description: String {
return result.debugDescription
}
/// The debug textual representation used when written to an output stream, which includes the URL request, the URL
/// response, the temporary and destination URLs, the resume data, the response serialization result and the
/// timeline.
public var debugDescription: String {
var output: [String] = []
output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")
output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")")
output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")")
output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes")
output.append("[Result]: \(result.debugDescription)")
output.append("[Timeline]: \(timeline.debugDescription)")
return output.joined(separator: "\n")
}
}
protocol Response
protocol Response {
/// The task metrics containing the request / response statistics.
var _metrics: AnyObject? { get set }
mutating func add(_ metrics: AnyObject?)
}
extension Response {
mutating func add(_ metrics: AnyObject?) {
#if !os(watchOS)
guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return }
guard let metrics = metrics as? URLSessionTaskMetrics else { return }
_metrics = metrics
#endif
}
}
// MARK: -
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DefaultDataResponse: Response {
#if !os(watchOS)
/// The task metrics containing the request / response statistics.
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DataResponse: Response {
#if !os(watchOS)
/// The task metrics containing the request / response statistics.
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DefaultDownloadResponse: Response {
#if !os(watchOS)
/// The task metrics containing the request / response statistics.
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DownloadResponse: Response {
#if !os(watchOS)
/// The task metrics containing the request / response statistics.
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}
上邊的協(xié)議中有一個(gè)屬性和一個(gè)方法,如果在協(xié)議中實(shí)現(xiàn)了自身的方法,那么實(shí)現(xiàn)該協(xié)議的對(duì)象可以不用實(shí)現(xiàn)該協(xié)議中的方法。在上邊介紹的屬性中 _metrics是來(lái)自該協(xié)議的屬性。在上邊的初始化方法中也沒(méi)有_metrics這一項(xiàng)
在swift中,當(dāng)多個(gè)對(duì)象公用一個(gè)屬性或者方法時(shí),就可以考慮協(xié)議了。
在這里按照上邊的用法,舉個(gè)簡(jiǎn)單的例子。
public struct Person {
public var name: String
public var age: UInt
var _hobby: String?
init(name: String, age: UInt) {
self.name = name
self.age = age
}
}
var person = Person(name: "James", age: 30)
print(person.name)
person.name = "Bond"
print(person.name)
var person1 = person
print(person1.name)
person1.name = "Rose"
print(person1.name)
print(person.name)
protocol Hobbyable {
var _hobby: String? { get set }
mutating func addHobby(_ hobby: String?)
}
extension Hobbyable {
mutating func addHobby(_ hobby: String?) {
_hobby = hobby
}
}
extension Person: Hobbyable {
var hobby: String? {
return _hobby
}
}
person1.addHobby("Books")
print(person1.hobby ?? "")
總結(jié)
由于知識(shí)水平有限,如有錯(cuò)誤,還望指出
鏈接
Alamofire源碼解讀系列(一)之概述和使用 簡(jiǎn)書(shū)-----博客園
Alamofire源碼解讀系列(二)之錯(cuò)誤處理(AFError) 簡(jiǎn)書(shū)-----博客園
Alamofire源碼解讀系列(三)之通知處理(Notification) 簡(jiǎn)書(shū)-----博客園
Alamofire源碼解讀系列(四)之參數(shù)編碼(ParameterEncoding) 簡(jiǎn)書(shū)-----博客園
Alamofire源碼解讀系列(五)之結(jié)果封裝(Result) 簡(jiǎn)書(shū)-----博客園
Alamofire源碼解讀系列(六)之Task代理(TaskDelegate) 簡(jiǎn)書(shū)-----博客園
Alamofire源碼解讀系列(七)之網(wǎng)絡(luò)監(jiān)控(NetworkReachabilityManager) 簡(jiǎn)書(shū)-----博客園
Alamofire源碼解讀系列(八)之安全策略(ServerTrustPolicy) 簡(jiǎn)書(shū)-----博客園