代码示例: https://github.com/johnlui/Swift-On-iOS/blob/master/BuildYourHTTPRequestLibrary
开源项目:Pitaya,适合大文件上传的 HTTP 请求库: https://github.com/johnlui/Pitaya
本文中,我们将一起降低之前代码的耦合度,并使用适配器模式实现一层独立于底层结构的网络 API,造一个真正的网络请求“库”。
现在的清汤挂面式的代码虽然便于理解,但是功能单一,代码杂乱。我们一起来分析 NSURLSession 的使用过程:
在 Network 下另外新建一个 NetworkManager 类,将 URL、params、files 等设为成员变量,让他们在构造函数中初始化:
class NetworkManager { let method: String! let params: Dictionary<String, AnyObject> let callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void let session = NSURLSession.sharedSession() let url: String! var request: NSMutableURLRequest! var task: NSURLSessionTask! init(url: String, method: String, params: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>(), callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) { self.url = url self.request = NSMutableURLRequest(URL: NSURL(string: url)!) self.method = method self.params = params self.callback = callback } }
之后,将上面分析的
1. 确定 URL
2. 确定 HTTP 方法(GET、POST 等)
3. 添加特定的 HTTP 头
4. 填充 HTTP Body
前三步封装到一个 function 中,最后一步封装到一个 function 中,然后把驱动 session.dataTaskWithRequest 的代码封装到一个 function 中:
func buildRequest() { if self.method == "GET" && self.params.count > 0 { self.request = NSMutableURLRequest(URL: NSURL(string: url + "?" + buildParams(self.params))!) } request.HTTPMethod = self.method if self.params.count > 0 { request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") } } func buildBody() { if self.params.count > 0 && self.method != "GET" { request.HTTPBody = buildParams(self.params).nsdata } } func fireTask() { task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in self.callback(data: data, response: response, error: error) }) task.resume() }
之后使用一个统一的方法来驱动上面三个 function,完成请求:
func fire() { buildRequest() buildBody() fireTask() }
同时,不要忘了那三个 parse params 的从 Alamofire 偷来的函数哦,也要放到这个类里面。至此,降低耦合的工作基本完成,接下来我们开始封装“网络API”。
适配器模式是设计模式中的一种,很容易理解:我的 APP 需要一个获取某一个 URL 返回的字符串的功能,我现在选择的是 Alamofire,但是正在发展的 Pitaya 看起来不错,我以后想替换成 Pitaya,所以我封装了一层我自己的网络接口,用来屏蔽底层细节,到时候只需要修改这个类,不需要再深入项目中改那么多接口调用了。
适配器模式听起来高大上,其实这是我们在日常编码中非常常用的设计模式。
修改 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 manager = NetworkManager(url: url, method: method, params: params, callback: callback) manager.fire() } }
搞定!
不带 params 的接口:
static func request(method: String, url: String, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) { let manager = NetworkManager(url: url, method: method, callback: callback) manager.fire() }
两个 get 接口(带与不带 params):
static func get(url: String, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) { let manager = NetworkManager(url: url, method: "GET", callback: callback) manager.fire() } static func get(url: String, params: Dictionary<String, AnyObject>, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) { let manager = NetworkManager(url: url, method: "GET", params: params, callback: callback) manager.fire() }
两个 post 接口(带与不带 params):
static func post(url: String, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) { let manager = NetworkManager(url: url, method: "POST", callback: callback) manager.fire() } static func post(url: String, params: Dictionary<String, AnyObject>, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) { let manager = NetworkManager(url: url, method: "POST", params: params, callback: callback) manager.fire() }
修改 ViewController 中的调用代码,测试多级 API:
@IBAction func mainButtonBeTapped(sender: AnyObject) { let url = "http://pitayaswift.sinaapp.com/pitaya.php" Network.post(url, callback: { (data, response, error) -> Void in println("POST 1 请求成功") }) Network.post(url, params: ["post": "POST Network"], callback: { (data, response, error) -> Void in let string = NSString(data: data, encoding: NSUTF8StringEncoding) as! String println("POST 2 请求成功 " + string) }) Network.get(url, callback: { (data, response, error) -> Void in println("GET 1 请求成功") }) Network.get(url, params: ["get": "POST Network"], callback: { (data, response, error) -> Void in let string = NSString(data: data, encoding: NSUTF8StringEncoding) as! String println("GET 2 请求成功 " + string) }) Network.request("GET", url: url, params: ["get": "Request Network"]) { (data, response, error) -> Void in let string = NSString(data: data, encoding: NSUTF8StringEncoding) as! String println("Request 请求成功 " + string) } }
运行项目,点击按钮,查看效果:
下一步:自己动手写一个 iOS 网络请求库(四)——快速文件上传【待更新】