代码示例: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()
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
修改 ViewController 中的按钮函数:
@IBAction func mainButtonBeTapped(sender: AnyObject) {
Network.request()
}
- 1.
- 2.
- 3.
运行项目,点击按钮,效果和之前一致。
自定义 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()
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
修改前面的函数调用:
@IBAction func mainButtonBeTapped(sender: AnyObject) {
Network.request("GET", url: "http://baidu.com")
}
- 1.
- 2.
- 3.
运行项目,点击按钮,效果和之前一致。
使用闭包处理请求结果
函数是 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()
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
在前面函数调用处使用闭包进行结果处理:
@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)
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
运行项目,点击按钮,效果和之前一致。
动态增加 Params
GET 方法
GET 方法下,params 在经过 url encode 之后直接附在 URL 末尾发送给服务器。修改 request() 方法,传递进去一个 params 的字典:
static func request(method: String, url: String, params: Dictionary = Dictionary(), callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {
... ...
}
- 1.
- 2.
- 3.
为了处理 params,我们从 Alamofire 偷来他的 params 处理函数。如果是 GET 方法,那就把处理过的 params 增加到 URL 后面。Network 类的完整代码如下:
class Network{
static func request(method: String, url: String, params: Dictionary = Dictionary(), 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), [(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
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
修改前面的函数调用:
@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)
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
http://pitayaswift.sinaapp.com/pitaya.php 是我部署的用于测试的服务端代码,会直接返回 ?get=ooxx 中的 ooxx。运行项目,点击按钮,查看效果:
POST 方法
POST 方法下有几个协议可供选择,此处没有文件上传,我们采用较简单的 application/x-www-form-urlencoded 方式发送请求。request() 方法增加一些代码:
static func request(method: String, url: String, params: Dictionary = Dictionary(), 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()
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
修改前面的函数调用:
@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)
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
使用 POST 方式发送请求,同样服务端会返回 key 为 post 的 value 的值。运行项目,点击按钮,结果和前面 GET 方法的结果一致。
至此,接口封装完成!