自己动手写一个 iOS 网络请求库(三)——降低耦合

2015-5-22 / 阅读数:16112 / 分类: iOS & Swift

本文中,我们将一起降低之前代码的耦合度,并使用适配器模式实现一层独立于底层结构的网络 API,造一个真正的网络请求“库”。

降低耦合度

如何降低耦合度

现在的清汤挂面式的代码虽然便于理解,但是功能单一,代码杂乱。我们一起来分析 NSURLSession 的使用过程:构造 NSURLRequest

确定 URL

确定 HTTP 方法(GET、POST 等)

添加特定的 HTTP 头

填充 HTTP Body

驱动 session.dataTaskWithRequest 方法,开始请求

具体实施

在 Network 下另外新建一个 NetworkManager 类,将 URL、params、files 等设为成员变量,让他们在构造函数中初始化:class NetworkManager {

let method: String!

let params: Dictionary

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 = Dictionary(), 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()

}

至此,降低耦合的工作基本完成,接下来我们开始封装“网络API”。

使用适配器模式封装“网络API”

理解适配器模式

适配器模式是设计模式中的一种,很容易理解:我的 APP 需要一个获取某一个 URL 返回的字符串的功能,我现在选择的是 Alamofire,但是正在发展的 Pitaya 看起来不错,我以后想替换成 Pitaya,所以我封装了一层我自己的网络接口,用来屏蔽底层细节,到时候只需要修改这个类,不需要再深入项目中改那么多接口调用了。

适配器模式听起来高大上,其实这是我们在日常编码中非常常用的设计模式。

Do it!

修改 Network 类的代码为:class Network{

static func request(method: String, url: String, params: Dictionary = Dictionary(), 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, 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, 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)

}

}

运行项目,点击按钮,查看效果:

多级 API 封装成功!

WRITTEN BY

程序员,Swift Contributor,正在写《iOS 可视化编程与 Auto Layout》。

评论:

iOSQiao

2016-04-27 15:33

我这样使用会报错:

Network.request("GET", url: "http://192.168.1.111/network.php") { (data, response, error) -> Void in

let string = NSString(data: data!, encoding: NSUTF8StringEncoding)

print(string)

}

错误提示: Ambiguous use of 'request(_:url:callback:)'

加上params就行,请问博主,这是为什么?

KangKai

2015-10-01 19:39

吕老师,我想请问下,以get方法为例,不知道这样写是不是更简洁,还是说你那样写有别的考虑?

//两个 get 接口(带与不带 params)

static func get(url: String, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {

self.get(url, params: Dictionary(),  callback: callback)

}

static func get(url: String, params: Dictionary, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {

let manager = NetworkManager(url: url, method: "GET", params: params, callback: callback)

manager.fire()

}

2015-10-01 22:39

@KangKai:当时这么写是因为 Swift 编译器的代码提示工具做的还不是很好,现在直接给参数设置默认值,在代码提示里就会直接显示两个 api 了。

那爱离殇

2015-06-02 13:34

本人对封装网络库有一个疑问,就是要不要在封装的API里面加上网络请求的风火轮以及错误信息抛出?如果不加的话,那要在每个用到的地方都要加,如果加上的话,可能个别地方又不需要加!请指教^_^

2015-06-02 15:23

@那爱离殇:网络库只是一个底层库,只完成一件事。对于具体的业务,上层是可以再封装的。UI 上的菊花会动和错误信息抛出建议每个接口都单独处理。

那爱离殇

2015-06-02 15:30

@JohnLui:

那简直让人崩溃呀,每个地方都写!!!

2015-06-02 15:54

@那爱离殇:业务需要嘛

cclv

2015-05-31 21:31

小弟遇到了一个问题:

报错:

ambiguous use of 'post'

ambiguous use of 'get'

下边这俩方法

Network.post(url, callback: { (data, response, error) -> Void in

println("POST 1 请求成功")

})

Network.get(url, callback: { (data, response, error) -> Void in

println("GET 1 请求成功")

})

2015-05-31 21:43

@cclv:目测是因为:

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, callback: (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void) {

let manager = NetworkManager(url: url, method: "POST", params: params, callback: callback)

manager.fire()

}

写成了一样的参数,其实是不一样的。

发表评论:

昵称

邮件地址 (选填)

个人主页 (选填)

吕文翰 php,自己动手写一个 iOS 网络请求库(三)——降低耦合相关推荐

  1. 写一个nodejs 网络请求

    我可以给你一个简单的nodejs网络请求的例子: const http = require('http'); const req = http.request({ hostname: 'example ...

  2. 吕文翰 php,自己动手打造基于 WKWebView 的混合开发框架(二)——js 向 Native 一句话传值并反射出 Swift 对象执行指定函数...

    自己动手打造基于 WKWebView 的混合开发框架(二)--js 向 Native 一句话传值并反射出 Swift 对象执行指定函数 2015-9-2 / 阅读数:39757 / 分类: iOS & ...

  3. 自己动手写一个印钞机 第四章

    2019独角兽企业重金招聘Python工程师标准>>> 作者:阿布? 未经本人允许禁止转载 ipython notebook git版本 目录章节地址: 自己动手写一个印钞机 第一章 ...

  4. 自己动手写一个印钞机 第二章

    2019独角兽企业重金招聘Python工程师标准>>> 作者:阿布? 未经本人允许禁止转载 ipython notebook git版本 目录章节地址: 自己动手写一个印钞机 第一章 ...

  5. 自己动手写一个印钞机 第一章

    2019独角兽企业重金招聘Python工程师标准>>> 作者:阿布? 未经本人允许禁止转载 ipython notebook git版本 目录章节地址: 自己动手写一个印钞机 第一章 ...

  6. 小飞鱼通达二开 软件版权登记之自己动手写一个源代码文档生成器(图文)

    软件开发过后,如果需要对软件进行保护就需要进行版权登记,现在登记都可以在网上进行也是非常的方便,在版权登记网站登记信息后,需要打印一份表格签字,然后就是需要准备软件的设计说明书和打印源代码2个事情. ...

  7. 自己动手写一个推荐系统,推荐系统小结,推荐系统:总体介绍、推荐算法、性能比较, 漫谈“推荐系统”, 浅谈矩阵分解在推荐系统中的应用...

    自己动手写一个推荐系统 废话: 最近朋友在学习推荐系统相关,说是实现完整的推荐系统,于是我们三不之一会有一些讨论和推导,想想索性整理出来. 在文中主要以工程中做推荐系统的流程着手,穿插一些经验之谈,并 ...

  8. 自己动手写一个仿Docker虚拟容器

    自己动手写一个仿Docker虚拟容器 本项目参照书籍<自己动手写Docker> 作者:陈显鹭(花名:遥鹭)-阿里云高级研发工程师等 项目地址:https://gitee.com/Sheng ...

  9. java 手编线程池_死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

最新文章

  1. 日志系统新贵Loki,确实比笨重的ELK轻
  2. 题目1024:畅通工程
  3. word 另存为xml后,读取xml的图片数据
  4. C++ Primer 5th笔记(chap 16 模板和泛型编程)转发参数包
  5. ubuntu14.04管理员密码忘记的解决方法
  6. ASCII表完整版(包含16进制对应表)
  7. 2020蓝桥杯省赛---java---B---6(分类计数)
  8. 向量积 和 它的计算_7
  9. 羡慕,浙江大学的双 11 快递,全部由物流机器人配送
  10. Android 广播接收器注册与注销源码分析
  11. cactiez v11使用配置mysql_cactiez应用监控部署手册.doc
  12. 解释Spring中IOC, DI, AOP
  13. Atitit.javascript 实现类的方式原理大总结
  14. C++标准委员会7月科隆会议中投票通过的特性
  15. html如何在搜索栏里加放大镜,css 如何在html页面上输出一个“放大镜”形状呢?...
  16. Cryengine5.3
  17. Java学习1——计算机基础知识
  18. 福莱特法公式matlab,浙江省公路出行交通分布模型研究
  19. ecshop mysql 报错_修复ecshop数据库ecs_sessions.MYI报错
  20. 撰写论文时常用的研究方法有哪些?

热门文章

  1. .net开发 abp框架+vue的微信公众号的微信授权
  2. 香肠派对手游怎么在电脑上玩 香肠派对电脑版玩法教程
  3. 60天造个火箭给你玩玩,你想要不?
  4. KUDU和IMPALA的结合使用
  5. 眼袋、眼袋、眼袋!眼袋一直有~~~~ 肿么办啊
  6. 用技术支点撬开“掘金池”,多多云能否成为中国版VMWare?
  7. 写 Python 爬虫的助手
  8. 程序员福利---免费接口
  9. PX4平台(V3)+T8S遥控器校准
  10. http协议相关内容,C/S与B/S,ip报文,TCP,UDP