美文网首页ios 开发iOSSwift
Swift--怎样更优雅地处理网络返回数据

Swift--怎样更优雅地处理网络返回数据

作者: 日光镇 | 来源:发表于2015-01-30 13:17 被阅读12340次

    一、过去这么干

    我们在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>

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

    二、在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)
    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)
    }
    }
    </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>

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

    三、现在应该这么干

    不过,如果你现在把以上代码拷贝进你运行在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>

    相关文章

      网友评论

      本文标题:Swift--怎样更优雅地处理网络返回数据

      本文链接:https://www.haomeiwen.com/subject/vvelxttx.html