Response Alamofire

2017-11-10  本文已影响17人  水落斜阳

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)")
    }

因为有4中不同的Request类型,StreamRequest我们先不提,对于UploadRequest来说,服务器响应的数据比较简单,就响应一个结果就行,因此不需要对它的Response专门进行封装。因此,Alamofire设计了2中与之相对应的Response类型,他们分别是:
DefaultDataResponse / DataResponse
DefaultDownloadResponse / DataResponse

// 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

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<Value>

DataResponse<Value>比上边的DefaultDataResponse多了一个result属性,该属性存储了序列化后的数据。它的类型是 Result<Value>

/// 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实现了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

DefaultDownloadResponse保存的是下载任务的数据。有3个属性需要做一下介绍:

(1)temporaryURL: URL? 现在成功后,数据会被保存在这个临时URL中

(2)destinationURL: URL? 目标URL,如果设置了该属性,那么文件会复制到该URL中

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

DownloadResponse

这个也没什么好说的,直接上代码:

/// 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
}

上边的协议中有一个属性和一个方法,如果在协议中实现了自身的方法,那么实现该协议的对象可以不用实现该协议中的方法。在上边介绍的属性中 _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 ?? "")
上一篇下一篇

猜你喜欢

热点阅读