本篇主要带来Alamofire中Response的解读html
在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提早讲一下。我更想同你们分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?固然,这些理解也都是基于我本身的理解。不免具备局限性。swift
当咱们设计完一个Request的时候,咱们确定要处理服务器返回的响应数据。在Alamofire源码解读系列(一)之概述和使用中,咱们已经讲过,Alamofire中把Request分为了4类:api
Alamofire中Request可使用链式访问:安全
Alamofire.request("https://httpbin.org/get") .responseString { response in print("Response String: \(response.result.value)") } .responseJSON { response in print("Response JSON: \(response.result.value)") }
能实现链式访问的原理就是每一个函数的返回值都是Self
。那么在上边的代码中,虽然response
的名字都同样,但并非同一类型。服务器
由于有4中不一样的Request类型,StreamRequest咱们先不提,对于UploadRequest来讲,服务器响应的数据比较简单,就响应一个结果就行,所以不须要对它的Response专门进行封装。所以,Alamofire设计了2中与之相对应的Response类型,他们分别是:网络
那么,若是咱们使用下边的代码获取响应数据:app
// Response Handler - Unserialized Response func response( queue: DispatchQueue?, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self
获取的是没有通过序列化后的数据,若是使用了没有序列化的response方法,返回的就是带有Default
开头的响应者,好比DefaultDataResponse,DefaultDownloadResponse。若是使用了序列化的response方法,返回的就是DataResponse或者DataResponse。框架
这说明了什么? 在程序的设计层面上,这种先后呼应的手法可以让代码更好理解。就像在项目中不能把全部的参数都放到一个模型中同样。函数
拿DefaultDataResponse / DataResponse来举例,DataResponse基本上只比DefaultDataResponse多了一个系列化后的数据属性。编码
还有一点要提一下,先假设每一个Request均可以被序列化为JSON,或者String。这些都属于序列化Response的范围。序列化成功后,保存数据的容器应该有一个类型,而这个类型又是能够变化的,在本篇文章下边的内容中会指出泛型的使用方法。
DefaultDataResponse用于存储data或upload请求状况下的全部无序列化的数据。那么在Alamofire中对于服务器的响应主要关心的数据有下边几个:
request: URLRequest?
表示该响应来源于那个请求response: HTTPURLResponse?
服务器返回的响应data: Data?
响应数据error: Error?
在请求中可能发生的错误timeline: Timeline
请求的时间线封装,这个会在后续的文章中解释_metrics: AnyObject?
包含了请求和响应的统计信息代码以下:
/// 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?
通常来讲,在swift中,若是只是为了保存数据,那么应该把这个类设计成struct。struct是 值传递,所以对数据的操做更安全。除了定义须要保存的数据属性后,必须设计一个符合要求的构造函数。
/// 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
/// 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的打印信息。
咱们也能够给咱们模型实现这两个协议,在代码调试的时候,打印出详细的信息,比打断点来查看效率更高。
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保存的是下载任务的数据。有3个属性须要作一下介绍:
temporaryURL: URL?
如今成功后,数据会被保存在这个临时URL中destinationURL: URL?
目标URL,若是设置了该属性,那么文件会复制到该URL中resumeData: Data?
表示可恢复的数据,对于下载任务,若是由于某种缘由下载中断了,或失败了,可使用该数据恢复以前的下载其余的内容跟上边介绍的内容没什么特别的地方,就简单的把代码弄上来了:
/// 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 } }
这个也没什么好说的,直接上代码:
/// 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 } }
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 { /// 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 }
上边的协议中有一个属性和一个方法,若是在协议中实现了自身的方法,那么实现该协议的对象能够不用实现该协议中的方法。**在上边介绍的属性中 _metrics是来自该协议的属性。在上边的初始化方法中也没有_metrics这一项**
在swift中,当多个对象公用一个属性或者方法时,就能够考虑协议了。
在这里按照上边的用法,举个简单的例子。
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 ?? "")
因为知识水平有限,若有错误,还望指出
Alamofire源码解读系列(一)之概述和使用 简书-----博客园
Alamofire源码解读系列(二)之错误处理(AFError) 简书-----博客园
Alamofire源码解读系列(三)之通知处理(Notification) 简书-----博客园
Alamofire源码解读系列(四)之参数编码(ParameterEncoding) 简书-----博客园
Alamofire源码解读系列(五)之结果封装(Result) 简书-----博客园
Alamofire源码解读系列(六)之Task代理(TaskDelegate) 简书-----博客园
Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager) 简书-----博客园