在 swift中处理网络请求

来源:互联网 发布:手机修图软件 编辑:程序博客网 时间:2024/05/22 13:14

一、过去这么干

我们在iOS开发中进行网络请求的时候,一般是获取到服务器返回的data后,再根据我们的需要转换成JSON,图像等信息:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. let myURL = NSURL(string: "http://imgs.xkcd.com/comics/scrabble.png")!  
  2.        let task = NSURLSession.sharedSession().dataTaskWithURL(myURL, completionHandler: { (data, response, error) -> Void in  
  3.            let image = UIImage(data: data)  
  4.            //use the image  
  5.        })  

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

二、在Swift-beta时代

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

首先我们建立一个protocol:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public protocol ResponseConvertible{  
  2.     class func convertFromData(data:NSData!) -> (Self?,NSError?)  
  3. }  

这个protocol负责把网络请求返回的NSData转化成我们想要的任何类型。 相信大家应该注意到了返回中的Self了,把返回定义为Self是因为这个接口本身是没有包含类型信息的,我们并不知道哪个类会实现这个接口,所以我们使用Self来指代将要实现这个接口的类。 接着,我们使用extension来为JSON(我使用的是SwiftJSON),UIImage,NSData这三个类实现我们的ResponseConvertible接口:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. extension JSON:ResponseConvertible{  
  2.     public static func convertFromData(data:NSData!) -> (JSON?, NSError?){  
  3.         let value = JSON(data: data, options: NSJSONReadingOptions.MutableContainers, error: nil)  
  4.         switch value.type{  
  5.         case .Null:  
  6.             return (value, value.error)  
  7.         default:  
  8.             return (value, nil)  
  9.         }  
  10.     }  
  11. }  
  12. extension NSData:ResponseConvertible{  
  13.     public class func convertFromData(data: NSData!) -> (NSData?, NSError?) {  
  14.         return (data,nil)  
  15.     }  
  16. }  
  17. extension UIImage:ResponseConvertible{  
  18.     public typealias Result = UIImage  
  19.     public class func convertFromData(data: NSData!) -> (UIImage?, NSError?) {  
  20.         return (UIImage(data: data),nil)  
  21.     }  
  22. }  

然后,我们再新建一个名为MyRequest的自定义的网络请求类:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class MyRequest <  T:ResponseConvertible> {    
  2.     var url:NSURL    
  3.     init(url:NSURL) {    
  4.         self.url = url    
  5.     }    
  6.     //这里用一个简单的请求进行说明,实际应用中可以构建一个最适合你的网络请求框架    
  7.     func aSimpleRequest(completionHandler:(T?,NSError!) -> ()){    
  8.         let session = NSURLSession.sharedSession()    
  9.         let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in    
  10.             if error == nil{    
  11.                 //用ResponseConvertible接口进行数据转换    
  12.                 let(object, converError) = T.convertFromData(data)    
  13.                 completionHandler(object,converError)    
  14.             }    
  15.         })    
  16.         task.resume()    
  17.     }    
  18. }    

好了,准备工作完成,我们终于可以使用优化后的请求方法了。请求时,< T:ResponseConvertible>接受一个实现了ResponseConvertible接口的类型,例如UIImage:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. let myURL = NSURL(string: "http://imgs.xkcd.com/comics/scrabble.png")!  
  2.         MyRequest<  UIImage>(url: myURL).aSimpleRequest({image,error in  
  3.             self.imageView.image = image  
  4.         })  

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

三、现在应该这么干

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


错误详情:

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

而这些代码在Swift-beta时代是能正常运行的。看来苹果在Swift1.0中对Self的用法进行了修改。

根据错误的提示,我们需要把使用了Self的类定义为final,而这在自定义的类中的确有用:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. final class MyClass: {  
  2.     //...  
  3. }  

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

最后,还是在typealias的帮助下绕过了,typealias 可以为已经存在的类型和方法重新定义一个快捷名字,比如:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. typealias Result = UIImage  

这里的Result就相当于UIImage类型了。 引入typealias后,以上的代码做如下修改,就能在新版本的Xcode下运行了:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public protocol ResponseConvertible{  
  2.     typealias Result  
  3.     class func convertFromData(data:NSData!) -> (Result?,NSError?)  
  4. }  
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. extension JSON:ResponseConvertible{  
  2.     public typealias Result = JSON  
  3.     public static func convertFromData(data:NSData!) -> (Result?, NSError?){  
  4.         let value = JSON(data: data, options: NSJSONReadingOptions.MutableContainers, error: nil)  
  5.         switch value.type{  
  6.         case .Null:  
  7.             return (value, value.error)  
  8.         default:  
  9.             return (value, nil)  
  10.         }  
  11.     }  
  12. }  
  13. extension NSData:ResponseConvertible{  
  14.     public typealias Result = NSData  
  15.     public class func convertFromData(data: NSData!) -> (NSData?, NSError?) {  
  16.         return (data,nil)  
  17.     }  
  18. }  
  19. extension UIImage:ResponseConvertible{  
  20.     public typealias Result = UIImage  
  21.     public class func convertFromData(data: NSData!) -> (UIImage?, NSError?) {  
  22.         return (UIImage(data: data),nil)  
  23.     }  
  24. }  
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class MyRequest< T:ResponseConvertible> {  
  2.     //这里用一个简单的请求说明,实际应用中可以构建一个最适合你的网络请求框架  
  3.     var url:NSURL  
  4.     init(url:NSURL) {  
  5.         self.url = url  
  6.     }  
  7.     func aSimpleRequest(completionHandler:(T.Result?,NSError!) -> ()){  
  8.         let session = NSURLSession.sharedSession()  
  9.         let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in  
  10.             if error == nil{  
  11.                 let(object, converError) = T.convertFromData(data)  
  12.                 completionHandler(object,converError)  
  13.             }  
  14.         })  
  15.         task.resume()  
  16.     }  
  17. }  

使用:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. let myURL = NSURL(string: "http://imgs.xkcd.com/comics/scrabble.png")!  
  2.         MyRequest<  UIImage>(url: myURL).aSimpleRequest({image,error in  
  3.             self.imageView.image = image  
  4.         })  
0 0