转载

自己动手写一个 iOS 网络请求库(二)——封装接口

代码示例: https://github.com/johnlui/Swift-On-iOS/blob/master/BuildYourHTTPRequestLibrary

开源项目:Pitaya,适合大文件上传的 HTTP 请求库: https://github.com/johnlui/Pitaya

本篇文章中,我们将一起尝试使用一个类来封装我们之前的代码,并尝试加入动态增加 HTTP 参数(params)的功能,之后封装出一个强大的接口。

基本封装

基础准备

新建一个 Swift 空文件,命名为 Network.swift,在里面写一个 Network 类,之后写一个静态方法 request():

class Network{  static func request() {   let session = NSURLSession.sharedSession()   let request = NSURLRequest(URL: NSURL(string: "http://baidu.com")!)   let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in    println("just wait for 5 seconds!")    sleep(5)    let string = NSString(data: data, encoding: NSUTF8StringEncoding)    println(string)   })   task.resume()  } } 

修改 ViewController 中的按钮函数:

@IBAction func mainButtonBeTapped(sender: AnyObject) {     Network.request() }

运行项目,点击按钮,效果和之前一致。

自定义 HTTP method 和 URL

修改 request() 方法,将 HTTP 方法和 URL 传进去:

static func request(method: String, url: String) {  let session = NSURLSession.sharedSession()  let request = NSMutableURLRequest(URL: NSURL(string: url)!)  request.HTTPMethod = method  let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in   println("just wait for 5 seconds!")   sleep(5)   let string = NSString(data: data, encoding: NSUTF8StringEncoding)   println(string)  })  task.resume() } 

修改前面的函数调用:

@IBAction func mainButtonBeTapped(sender: AnyObject) {     Network.request("GET", url: "http://baidu.com") }

运行项目,点击按钮,效果和之前一致。

使用闭包处理请求结果

函数是 Swift 中的一等公民,闭包可以作为函数参数和返回值,十分强大。下面我们就用闭包来处理网络请求的返回值。修改 request() 方法,传递进去一个闭包:

static func request(method: String, url: String, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {  let session = NSURLSession.sharedSession()  let request = NSMutableURLRequest(URL: NSURL(string: url)!)  request.HTTPMethod = method  let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in   callback(data: data, response: response , error: error)  })  task.resume() } 

在前面函数调用处使用闭包进行结果处理:

@IBAction func mainButtonBeTapped(sender: AnyObject) {  Network.request("GET", url: "http://baidu.com") { (data, response, error) -> Void in   println("just wait for 5 seconds!")   sleep(5)   let string = NSString(data: data, encoding: NSUTF8StringEncoding)   println(string)  } } 

运行项目,点击按钮,效果和之前一致。

动态增加 Params

GET 方法

GET 方法下,params 在经过 url encode 之后直接附在 URL 末尾发送给服务器。修改 request() 方法,传递进去一个 params 的字典:

static func request(method: String, url: String, params: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>(), callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {     ... ... }

为了处理 params,我们从 Alamofire 偷来他的 params 处理函数。如果是 GET 方法,那就把处理过的 params 增加到 URL 后面。Network 类的完整代码如下:

class Network{  static func request(method: String, url: String, params: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>(), callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {   let session = NSURLSession.sharedSession()   var newURL = url   if method == "GET" {    newURL += "?" + Network().buildParams(params)   }   let request = NSMutableURLRequest(URL: NSURL(string: newURL)!)   request.HTTPMethod = method   let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in    callback(data: data, response: response , error: error)   })   task.resume()  }  // 从 Alamofire 偷了三个函数  func buildParams(parameters: [String: AnyObject]) -> String {   var components: [(String, String)] = []   for key in sorted(Array(parameters.keys), <) {    let value: AnyObject! = parameters[key]    components += self.queryComponents(key, value)   }   return join("&", components.map{"/($0)=/($1)"} as [String])  }  func queryComponents(key: String, _ value: AnyObject) -> [(String, String)] {   var components: [(String, String)] = []   if let dictionary = value as? [String: AnyObject] {    for (nestedKey, value) in dictionary {     components += queryComponents("/(key)[/(nestedKey)]", value)    }   } else if let array = value as? [AnyObject] {    for value in array {     components += queryComponents("/(key)", value)    }   } else {    components.extend([(escape(key), escape("/(value)"))])   }   return components  }  func escape(string: String) -> String {   let legalURLCharactersToBeEscaped: CFStringRef = ":&=;+!@#$()',*"   return CFURLCreateStringByAddingPercentEscapes(nil, string, nil, legalURLCharactersToBeEscaped, CFStringBuiltInEncodings.UTF8.rawValue) as String  } } 

修改前面的函数调用:

@IBAction func mainButtonBeTapped(sender: AnyObject) {     Network.request("GET", url: "http://pitayaswift.sinaapp.com/pitaya.php", params: ["get": "Network"]) { (data, response, error) -> Void in         let string = NSString(data: data, encoding: NSUTF8StringEncoding)         println(string)     } }

http://pitayaswift.sinaapp.com/pitaya.php 是我部署的用于测试的服务端代码,会直接返回 ?get=ooxx 中的 ooxx。运行项目,点击按钮,查看效果:

自己动手写一个 iOS 网络请求库(二)——封装接口

POST 方法

POST 方法下有几个协议可供选择,此处没有文件上传,我们采用较简单的 application/x-www-form-urlencoded 方式发送请求。request() 方法增加一些代码:

static func request(method: String, url: String, params: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>(), callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {  let session = NSURLSession.sharedSession()  var newURL = url  if method == "GET" {   newURL += "?" + Network().buildParams(params)  }  let request = NSMutableURLRequest(URL: NSURL(string: newURL)!)  request.HTTPMethod = method  if method == "POST" {   request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")   request.HTTPBody = Network().buildParams(params).dataUsingEncoding(NSUTF8StringEncoding)  }  let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in   callback(data: data, response: response , error: error)  })  task.resume() } 

修改前面的函数调用:

@IBAction func mainButtonBeTapped(sender: AnyObject) {     Network.request("POST", url: "http://pitayaswift.sinaapp.com/pitaya.php", params: ["post": "Network"]) { (data, response, error) -> Void in         let string = NSString(data: data, encoding: NSUTF8StringEncoding)         println(string)     } }

使用 POST 方式发送请求,同样服务端会返回 key 为 post 的 value 的值。运行项目,点击按钮,结果和前面 GET 方法的结果一致。

至此,接口封装完成!

下一步:自己动手写一个 iOS 网络请求库(三)——降低耦合【待更新】

正文到此结束
Loading...