金沙澳门官网jin5888:怎样更优雅地处理网络返回数据,0源码解析

二、在Swift-beta时代

下面,我们使用swift中的泛型和extension让你的数据解析工作更优雅。

首先我们建立一个protocol:
<pre><code>
public protocol ResponseConvertible{
class func convertFromData(data:NSData!) -> (Self?,NSError?)
}
</code></pre>
这个protocol负责把网络请求返回的NSData转化成我们想要的任何类型。
相信大家应该注意到了返回中的Self了,把返回定义为Self是因为这个接口本身是没有包含类型信息的,我们并不知道哪个类会实现这个接口,所以我们使用Self来指代将要实现这个接口的类。
接着,我们使用extension来为JSON(我使用的是SwiftJSON),UIImage,NSData这三个类实现我们的ResponseConvertible接口:
<pre><code>
extension JSON:ResponseConvertible{
public static func convertFromData(data:NSData!) -> (JSON?,
NSError?){
let value = JSON(data: data, options:
NSJSONReadingOptions.MutableContainers, error: nil)
金沙澳门官网jin5888:怎样更优雅地处理网络返回数据,0源码解析。switch value.type{
case .Null:
return (value, value.error)
default:
return (value, nil)
}
}
}

extension NSData:ResponseConvertible{
public class func convertFromData(data: NSData!) -> (NSData?,
NSError?) {
return (data,nil)
}
}

extension UIImage:ResponseConvertible{
public typealias Result = UIImage
public class func convertFromData(data: NSData!) -> (UIImage?,
NSError?) {
return (UIImage(data: data),nil)
}
}
金沙澳门官网jin5888:怎样更优雅地处理网络返回数据,0源码解析。</code></pre>

然后,我们再新建一个名为MyRequest的自定义的网络请求类:
<pre><code>
class MyRequest < T:ResponseConvertible> {

var url:NSURL

init(url:NSURL) {
    self.url = url
}
//这里用一个简单的请求进行说明,实际应用中可以构建一个最适合你的网络请求框架
func aSimpleRequest(completionHandler:(T?,NSError!) -> ()){
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
        if error == nil{
            //用ResponseConvertible接口进行数据转换
            let(object, converError) = T.convertFromData(data)
            completionHandler(object,converError)
        }
    })
    task.resume()
}

}
</code></pre>

好了,准备工作完成,我们终于可以使用优化后的请求方法了。请求时,<
T:ResponseConvertible>接受一个实现了ResponseConvertible接口的类型,例如UIImage:
<pre><code>
let myURL = NSURL(string:
“http://imgs.xkcd.com/comics/scrabble.png”)!
MyRequest< UIImage>(url: myURL).aSimpleRequest({image,error in
self.imageView.image = image
})
</code></pre>

这样,我们只要在发起请求前定义好我们需要返回的类型,接口就会如愿地为你返回该类型的数据了。是不是比文章一开始的方法优雅多了?

<pre><code>enum RequestTask { case data(TaskConvertible?, URLSessionTask?) case download(TaskConvertible?, URLSessionTask?) case upload(TaskConvertible?, URLSessionTask?) case stream(TaskConvertible?, URLSessionTask?) }</code></pre>

/**
Adds a handler to be called once the request has finished.

金沙澳门官网jin5888:怎样更优雅地处理网络返回数据,0源码解析。一、过去这么干

我们在iOS开发中进行网络请求的时候,一般是获取到服务器返回的data后,再根据我们的需要转换成JSON,图像等信息:
<pre><code>
let myURL = NSURL(string:
“http://imgs.xkcd.com/comics/scrabble.png”)!
let task = NSURLSession.sharedSession().dataTaskWithURL(myURL,
completionHandler: { (data, response, error) -> Void in
let image = UIImage(data: data)
//use the image
})
</code></pre>

但如果你的应用中存在各种类型的返回数据,那么你可能就要在各个网络请求的中进行重复的处理了。

Request

Request初始化,保存了原始的Task任务和结束的绝对时间.
<pre><code>` init(session: URLSession, requestTask:
RequestTask, error: Error? = nil) {
self.session = session

    switch requestTask {
    case .data(let originalTask, let task):
        taskDelegate = DataTaskDelegate(task: task)
        self.originalTask = originalTask
    case .download(let originalTask, let task):
        taskDelegate = DownloadTaskDelegate(task: task)
        self.originalTask = originalTask
    case .upload(let originalTask, let task):
        taskDelegate = UploadTaskDelegate(task: task)
        self.originalTask = originalTask
    case .stream(let originalTask, let task):
        taskDelegate = TaskDelegate(task: task)
        self.originalTask = originalTask
    }

    delegate.error = error
    delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}`</code></pre>

TaskConvertible协议提供了一个接口返回URLSessionTask:
<pre><code>protocol TaskConvertible { func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask }</code></pre>

<pre><code>`open class DataRequest: Request {

// MARK: Helper Types

struct Requestable: TaskConvertible {
    let urlRequest: URLRequest

    func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
        do {
            let urlRequest = try self.urlRequest.adapt(using: adapter)
            return queue.sync { session.dataTask(with: urlRequest) }
        } catch {
            throw AdaptError(error: error)
        }
    }
}`</code></pre>

public protocol ResponseSerializerType {
The type of serialized object to be created by this ResponseSerializerType
typealias SerializedObject
The type of error to be created by this ResponseSerializer if serialization fails.
typealias ErrorObject: ErrorType
A closure used by response handlers that takes a request, response, data and error and returns a result
var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?,
NSError?)->Result<SerializedObject, ErrorObject> { get}
}

三、现在应该这么干

不过,如果你现在把以上代码拷贝进你运行在Xcode6正式版的项目,你会发现Xcode会报错:

xcode报错.png

错误详情:
<pre><code>
Protocol ‘ResponseConvertible’ requirement ‘convertFromData’ cannot be
satisfied by a non-final class (‘NSData’) because it uses ‘Self’ in a
non-parameter, non-result type position
</code></pre>

而这些代码在Swift-beta时代是能正常运行的。看来苹果在Swift1.0中对Self的用法进行了修改。
根据错误的提示,我们需要把使用了Self的类定义为final,而这在自定义的类中的确有用:
<pre><code>
final class MyClass: {
//…
}
</code></pre>

但是对于UIImage,NSData这些系统的类就无能为力了。这个问题困扰了我好一会,stackoverflow无果,请教一些盆友都表示对Self的用法不熟或压根没动过Swift。

最后,还是在typealias的帮助下绕过了,typealias
可以为已经存在的类型和方法重新定义一个快捷名字,比如
<pre><code>
typealias Result = UIImage
</code></pre>
这里的Result就相当于UIImage类型了。

引入typealias后,以上的代码做如下修改,就能在新版本的Xcode下运行了:

<pre><code>
public protocol ResponseConvertible{
typealias Result
class func convertFromData(data:NSData!) -> (Result?,NSError?)
}
</code></pre>

<pre><code>
extension JSON:ResponseConvertible{
public typealias Result = JSON
public static func convertFromData(data:NSData!) -> (Result?,
NSError?){
let value = JSON(data: data, options:
NSJSONReadingOptions.MutableContainers, error: nil)
switch value.type{
case .Null:
return (value, value.error)
default:
return (value, nil)
}
}
}

extension NSData:ResponseConvertible{
public typealias Result = NSData
public class func convertFromData(data: NSData!) -> (NSData?,
NSError?) {
return (data,nil)
}
}

extension UIImage:ResponseConvertible{
public typealias Result = UIImage
public class func convertFromData(data: NSData!) -> (UIImage?,
NSError?) {
return (UIImage(data: data),nil)
}
}
</code></pre>

<pre><code>
class MyRequest< T:ResponseConvertible> {

//这里用一个简单的请求说明,实际应用中可以构建一个最适合你的网络请求框架
var url:NSURL
init(url:NSURL) {
    self.url = url
}
func aSimpleRequest(completionHandler:(T.Result?,NSError!) -> ()){
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
        if error == nil{
            let(object, converError) = T.convertFromData(data)
            completionHandler(object,converError)
        }
    })
    task.resume()
}

}
</code></pre>

使用:
<pre><code>
let myURL = NSURL(string:
“http://imgs.xkcd.com/comics/scrabble.png”)!
MyRequest< UIImage>(url: myURL).aSimpleRequest({image,error in
self.imageView.image = image
})
</code></pre>

Response

Response中提供四个重要的结构体DefaultDataResponse
,DataResponse,DefaultDownloadResponse与DownloadResponse.

DataResponse扩展实现了CustomDebugStringConvertible协议,返回值的整体输出debugDescription.
<pre><code>`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")
}

}`</code></pre>

Alamofire的开发中最常见的代码如下:
<pre><code>`Alamofire.request(“https://httpbin.org/get”).responseJSON
{ response in
print(“Request: (String(describing: response.request))”) // original url
request
print(“Response: (String(describing: response.response))”) // http url
response
print(“Result: (response.result)”) // response serialization result

if let json = response.result.value {
    print("JSON: \(json)") // serialized json response
}

if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
    print("Data: \(utf8Text)") // original server data as UTF8 string
}

}`</code></pre>

responseJSON在Request和Response文件中都没有,将请求和响应的完美衔接的代码在ResponseSerialization.swift文件中.

<pre><code>extension DataRequest { /// Creates a response serializer that returns a JSON object result type constructed from the response data using ///JSONSerializationwith the specified reading options. /// /// - parameter options: The JSON serialization reading options. Defaults to.allowFragments`.
///
/// – returns: A JSON object response serializer.
public static func jsonResponseSerializer(
options: JSONSerialization.ReadingOptions = .allowFragments)
-> DataResponseSerializer<Any>
{
return DataResponseSerializer { _, response, data, error in
return Request.serializeResponseJSON(options: options, response:
response, data: data, error: error)
}
}

/// Adds a handler to be called once the request has finished.
///
/// - parameter options:           The JSON serialization reading options. Defaults to `.allowFragments`.
/// - parameter completionHandler: A closure to be executed once the request has finished.
///
/// - returns: The request.
@discardableResult
public func responseJSON(
    queue: DispatchQueue? = nil,
    options: JSONSerialization.ReadingOptions = .allowFragments,
    completionHandler: @escaping (DataResponse<Any>) -> Void)
    -> Self
{
    return response(
        queue: queue,
        responseSerializer: DataRequest.jsonResponseSerializer(options: options),
        completionHandler: completionHandler
    )
}

}`</code></pre>

response函数实现代码:
<pre><code>`extension DataRequest {
/// Adds a handler to be called once the request has finished.
///
/// – parameter queue: The queue on which the completion handler is
dispatched.
/// – parameter completionHandler: The code to be executed once the
request has finished.
///
/// – returns: The request.
@discardableResult
public func response(queue: DispatchQueue? = nil, completionHandler:
@escaping (DefaultDataResponse) -> Void) -> Self {
delegate.queue.addOperation {
(queue ?? DispatchQueue.main).async {
var dataResponse = DefaultDataResponse(
request: self.request,
response: self.response,
data: self.delegate.data,
error: self.delegate.error,
timeline: self.timeline
)

            dataResponse.add(self.delegate.metrics)

            completionHandler(dataResponse)
        }
    }

    return self
}

/// Adds a handler to be called once the request has finished.
///
/// - parameter queue:              The queue on which the completion handler is dispatched.
/// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
///                                 and data.
/// - parameter completionHandler:  The code to be executed once the request has finished.
///
/// - returns: The request.
@discardableResult
public func response<T: DataResponseSerializerProtocol>(
    queue: DispatchQueue? = nil,
    responseSerializer: T,
    completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
    -> Self
{
    delegate.queue.addOperation {
        let result = responseSerializer.serializeResponse(
            self.request,
            self.response,
            self.delegate.data,
            self.delegate.error
        )

        var dataResponse = DataResponse<T.SerializedObject>(
            request: self.request,
            response: self.response,
            data: self.delegate.data,
            result: result,
            timeline: self.timeline
        )

        dataResponse.add(self.delegate.metrics)

        (queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
    }

    return self
}

}`</code></pre>

public func responseJSON(options options: NSJSONReadingOptions =
.AllowFragments,completionHandler:Response<AnyObject, NSError>
->Void) -> Self {
return response(responseSerializer:
Request.JSONResponseSerializer(options: options),
completionHandler:completionHandler)
}
}

Alamofire中Request和Response是关于网络请求请求响应,Request有四种类型data,download,upload和stream类型,stream只有在iOS9
之后才可以使用.

}

Result

网络请求不管成功与否,最终都会有响应结果,Alamofire中的网络请求结果通过Result泛型枚举来表示.
<pre><code>`public enum Result<Value> {
case success(Value)
case failure(Error)

/// Returns `true` if the result is a success, `false` otherwise.
public var isSuccess: Bool {
    switch self {
    case .success:
        return true
    case .failure:
        return false
    }
}

/// Returns `true` if the result is a failure, `false` otherwise.
public var isFailure: Bool {
    return !isSuccess
}

/// Returns the associated value if the result is a success, `nil` otherwise.
public var value: Value? {
    switch self {
    case .success(let value):
        return value
    case .failure:
        return nil
    }
}

/// Returns the associated error value if the result is a failure, `nil` otherwise.
public var error: Error? {
    switch self {
    case .success:
        return nil
    case .failure(let error):
        return error
    }
}

}`</code></pre>

Result结果解包:
<pre><code>public func unwrap() throws -> Value { switch self { case .success(let value): return value case .failure(let error): throw error } }</code></pre>

Result扩展的map,flatMap函数:
<pre><code>` public func map<T>(_ transform: (Value)
-> T) -> Result<T> {
switch self {
case .success(let value):
return .success(transform(value))
case .failure(let error):
return .failure(error)
}
}

/// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
///
/// Use the `flatMap` method with a closure that may throw an error. For example:
///
///     let possibleData: Result<Data> = .success(Data(...))
///     let possibleObject = possibleData.flatMap {
///         try JSONSerialization.jsonObject(with: $0)
///     }
///
/// - parameter transform: A closure that takes the success value of the instance.
///
/// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the
///            same failure.
public func flatMap<T>(_ transform: (Value) throws -> T) -> Result<T> {
    switch self {
    case .success(let value):
        do {
            return try .success(transform(value))
        } catch {
            return .failure(error)
        }
    case .failure(let error):
        return .failure(error)
    }
}`</code></pre>

Swift水平有限,如有不当,欢迎多多指正~

  • parameter completionHandler: The code to be executed once the request
    has finished.
  • returns: The request.
    */
    public func responseData(completionHandler: Response<NSData,
    NSError> -> Void) -> Self {
    return response(responseSerializer: Request.dataResponseSerializer(),
    completonHandlerHandler: completionHandler)
    }
    }

}

相关文章