iOS项目之模拟请求数据
如何在iOS开发中更好的做假数据?
当工期比较紧的时候,项目开发中会经常出现移动端等待后端接口数据的情形,不但耽误项目进度,更让人有种无奈的绝望。所以在开发中,我们常常自己做些假数据,以方便开发和UI调试。然而做假数据方法不同,效率和安全性都各不同,有时稍有不慎,还会产生很大的bug。因此本文拟结合我在贝聊的开发经验,讲一讲我们组在iOS开发中曾经用过的做假数据的方法及其优劣。
示例项目
为方便下文的说明,本文主要以贝聊家长版app发现首页的热门帖子列表的实现为例。热门帖子列表的样式如下图:
这是比较常见的列表,用常用的UITableView实现即可,但需要自定义一个的UITableViewCell的子类ExploreTableViewCell。项目中,ExploreTableViewCell并没有用xib实现(因为xib日后不好修改,且代码复用性差),而是通过SnapKit用纯代码布的局,具体的布局代码大致如下:
1 |
import UIKit |
源码中写死
源码中写死数据是最便捷的假数据做法,项目很赶时,为最快速的看到UI效果,一般都会采取这种假数据方式。比如在上述热门帖子列表示例项目中,为查看整个ExploreTableViewCell的布局效果,在titleLabel等subview的设置位置,直接写死假数据。
1 |
//...其他代码 |
源码中写死假数据虽然方便,但稍有不慎就容易直接上线上环境(因为测试在测试时一般都会有数据,假数据被遮盖了),演变成一个有可能非常严重也有可能很轻的bug(贝聊就切实出现过这样的bug,而且还是个影响广泛的大bug),为安全起见,所有写死的假数据都应该包在条件编译宏内。
1 |
//...其他代码 |
包在条件编译宏内,就可以保证不污染正式环境的代码,从而保证安全性。
利用单元测试的网络请求stub做假数据
在源码中写死假数据,有以下三个缺点,
- 污染源代码
假数据写在源代码中,即使用宏包裹起来,只是保证了一定的安全性,但依然污染了源代码,如果上线前忘了把假数据代码移除,它一直会残留在源代码中,而且还会一直影响DEBUG环境的调试。 - 假数据散落四处,无法集中管理
本文示例代码的假数据虽然写在一块,但在实际开发中,并不是所有的UI代码都在一个文件中。即使在一个文件内,往往各个属性的初始化和设置也不在一个方法内。代码一多,基本很难管理。 - 扭曲了数据的正确流通
正确的数据产生方式,应该是发一个网络请求,然后把请求回来的数据转成model,最后通过model给各个UI组件填充数据。而在源代码中写死假数据,直接打乱了数据的正确流通,这会使得整个开发的逻辑是颠倒的,不但使开发更容易出bug,而且逻辑流的切换带来的开发效率和开发感受都很差。
较好的假数据方式,应该尽可能的不污染源代码,不扰乱正常的数据流通,而且能集中管理。在研究单元测试时,我无意中发现stub某个页面请求数据的网络请求即可达到这种完美的假数据效果。
首先按正常的流程开发整个功能,(在开发中,我总是倾下于先创建Model,而不是先写UI)
- 创建Model
- 创建ViewController
- 创建View等UI元素
- 在ViewController中完成网络请求的发起,并完成从网络数据到Model的转换
- 应用Model填充UI
整个功能开发按照有真实网络请求进行,但事实上并没有网络请求,因为后台并未搭好,没关系,先按照后台给出的接口和数据格式定义,创建一个本地JSON文件。对于本文的示例(假定只有列表数据)来说,文件名暂为hotTopics.json,内容大致如下(贝聊发现首页实际上有很多其他元素,网络请求返回的JSON也比这个复杂的多):
1 |
{
|
然后在ViewController中stub本ViewController中所有的网络请求,我在开发中用的是OHHTTPStubs,大致的代码如下:
1 |
class ExploreViewController: UITableViewController {
|
注意所创建的JSON文件一定要加到项目目录中。添加完上述代码后,path为/explore/hotTopics的网络请求将被stub,返回的数据将是所指定JSON文件中的数据,这样就跟真实的网络请求没有任何的区别了。而且利用OHHTTPStubs还可以模拟网络请求失败、网络请求超时以及throttle等各种网络请求状态,从而更全面的调试UI和整个功能。
利用stub做假数据可以真实的做到基本不污染代码、集中管理和完全真实的数据流通流程,与在源码中写死这种方式相比,近乎完美。然而当你真正用过一段时间后,你会发现,这种方式还是有一个致命的缺点和一个不那么重要的缺点。
- 不适合做UI调试
因为每改动一次数据,都需要重新编译,而iOS编译是很慢的,尤其是Swift。而要想做UI调试,频繁的改动数据,查看各种边界条件下的UI是必然的。 - 还是污染了代码
虽然相较上一种方法,污染非常小,但或多或少还是有污染的,有强迫症的人是受不了的,而且有时测试说是个bug(测试包一般是BETA环境),你build一下发现数据是假数据,不是网络请求的数据,还需要找到stub网络请求的位置,然后把代码注释了,也是极其的烦人的。
如果能做到每改动一下数据,然后刷新一下就可以看到了,像网页一样,而且真的一点都不污染代码,那就是完美的解决方案。
动态注入
如果只是想做到,每改动一下数据,然后刷新以下就可以看到了,像网页一样,Xcode的动态注入是可以的,现在比较流行的是 injectionforxcode和dyci-main两个库。利用单元测试的网络请求stub做假数据,然后结合动态注入,理论上应该可以做到实时刷新,但事实上injectionforxcode和dyci-main的体验都是很糟糕的,时灵时不灵,我用过两次后,基本就不想碰了,我宁愿编译慢一点,当然我从来没有用动态注入去做假数据的实时刷新,但我觉得应该是个方案。
但这个方案即使可行,也还是会污染代码,并不算特别彻底的方案。真正彻底的方案,与用stub拦截网络请求的思路相同,只是要将网络请求的拦截放到整个APP外,有两个方案可行。
本地自己搭个服务器
第一种就是本地自己搭个服务器,然后把开发时需要拦截的网络请求地址改为自己搭建的服务器地址,然后返回自己自定义的JSON数据。但这种方式也有三个缺点:
- 有一定门槛
虽然搭建服务器是很简单的事,并不是所有人都会,也是需要一定的学习成本的。 - 还是要修改源码中网络请求的地址
这虽然已经把源码污染降到最低了,但毕竟还是有。 - 要想模拟不同的网络状态,还需去修改服务器的代码,不方便。
综合起来这种方案性价比并不高,但确实有一定的趣味性,毕竟自己折腾东西嘛。
网络代理
第二种就是利用现有的网络代理软件,直接拦截对应的网络请求,然后返回本地写好的JSON数据。我最终采用的这种方案(因为我嫌配置服务器麻烦)。将APP中所有的网络请求都代理给网络代理,然后指定特定的网络请求返回本地JSON数据。这种方案的好处不言而喻,
- 真正的不污染源码
源码中任何代码都不用动,真正做到了干净绿色无污染。 - 拦截起来很方便
许多网络代理软件,都自带拦截甚至改写网络请求的功能,所以启动拦截功能很方便。 - 方便调试
网络代理一般都有改变一个网络请求状态的功能,可以轻松实现返回网络错误、网络超时和延迟网络请求等不同的网络请求状态的功能,非常方便。
我常用的网络代理就是Charles,相信大家都有耳闻。Charles有个maplocal的功能(在工具菜单下),如图:

mapLocal的设置也很简单,在Location一栏填上所要拦截的网络请求的host、path或者完整的URL,然后在LocalPath一栏选择对应的本地JSON文件即可,记得勾选启动。

这样简单的设置后,所指定的网络请求都会返回本地对应的JSON文件数据。然后你将发现这种假数据之完美,简直让人窒息。
编译后,如果想改变一个数据,看看对应的UI,直接去改变本地JSON文件,然后下来刷新一下,你会发现显示的数据就是刚刚改动的数据,简直要感动哭了。
但事实上这种方式还是有一个小小的缺点,即Charles与Shadowsocks不能同时开着,因为Charles不支持父代理。搞编程开发,为方便查阅资料,翻墙软件会一只开着,但这样Charles就不能开着,想用的话,又要先退出Shadowsocks,再打开Charles,这让我很头疼。最后只能在真正写完所有的逻辑和UI后,关闭Shadowsocks,打开Charles,集中调试。
=============2017-03-04更新开始===============
文章发出后,不少读者反馈,
- Charles与Shadowsocks可以共存
具体怎么共存,我还有待研究,后续文章再做补充 - 假数据的数据应该能随机生成
具体由json server、mock.js和Charles三者结合完成,有意思,值得研究,待后续文章详细补充。
=============2017-03-04更新结束===============
总结
一路试下来,其实只有第一种源码中写死和最后一种网络代理两种假数据方式最常用,虽然第一种缺点最多,但方便快捷,最后一种虽无任何缺点,但启动还是有点麻烦。
写了这么多,还是希望对大家有所启发。
(本文转载自轻墨,看了这篇文章受益匪浅,于是分享给大家。注重作者原创,已注明原文链接,点击上面标题可跳转!)
添加一个Demo:模拟请求数据
iOS项目之模拟请求数据的更多相关文章
- iOS关于XML解析请求数据
XML数据的请求: 和json请求几乎一样,只有请求参数修改为xml即可: AFHTTPSessionManager *manager = [AFHTTPSessionManager manager] ...
- iOS项目之解析HTML数据
最近因为需求,一直在做HTML数据的解析,从网页中去获取需要的数据,然后展示到自己的app中. 在网上找了很多资料,大多都是TFHpple这个第三方框架,能够根据标签节点获取对应的数据,但是现在我需要 ...
- iOS开发之Socket通信实战--Request请求数据包编码模块
实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数 据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncS ...
- VueJS搭建简单后台管理系统框架 (二) 模拟Ajax数据请求
开发过程中,免不了需要前台与后台的交互,大部分的交互都是通过Ajax请求来完成,在服务端未完成开发时,前端需要有一个可以模拟Ajax请求的服务器. 在NodeJs环境下,通过配置express可访问的 ...
- vue项目模拟后台数据
这次我们来模拟一些后台数据,然后去请求它并且将其渲染到界面上.关于项目的搭建鄙人斗胆向大家推荐我的一篇随笔<Vue开发环境搭建及热更新> 一.数据建立 我这里为了演示这个过程所以自己编写了 ...
- 聚合数据 iOS 项目开发实战:条码查询器
记录下,聚合数据 iOS 项目开发实战:条码查询器:视频地址:http://www.jikexueyuan.com/course/324.html 条码查询API:https://www.juhe.c ...
- 接口测试中模拟post四种请求数据
https://www.jianshu.com/p/3b6d7aa2043a 一.背景介绍 在日常的接口测试工作中,模拟接口请求通常有两种方法,fiddler模拟和HttpClient模拟. Fidd ...
- c# JD快速搜索工具,2015分析JD搜索报文,模拟请求搜索数据,快速定位宝贝排行位置。
分析JD搜索报文 搜索关键字 女装 第二页,分2次加载. rt=1&stop=1&click=&psort=&page=3http://search.jd.com/Se ...
- [转]Fiddler模拟post四种请求数据
1 前言 仅作为记录使用. 2 内容 post请求主体详解: 对于get请求来说没有请求主体entity-body.对于post请求而言,不会对发送请求的数据格式进行限制,理论上你可以发任意数据,但是 ...
随机推荐
- Android Weekly Notes Issue #245
Android Weekly Issue #245 February 19th, 2017 Android Weekly Issue #245 本期内容: 写好单元测试的几条原则; 如何mock Ko ...
- CMFCShellList和自定义ShellList结合使用,达到“直接浏览缩略图,双击打开图片”
在GOPaint的设计研究过程中,我一直希望能够实现这样的结果(A B C 3个步骤) 在我之前的博客里面,曾经有过缩略图显示的现就(http://www.cnblogs.com/jsxyhelu/p ...
- ASP.NET Web API 基本操作(CRUD)
上一篇介绍了ASP.NET Web API的基本知识和原理,这一篇我们通过一个更直观的实例,对产品进行CRUD操作(Create/Read/Update/Delete)来继续了解一下它的基本应用. 创 ...
- ES6-01:常量与变量的声明
首先,我们声明一个变量: //定义一个变量num,并赋值为10: let num = 10; //进行打印 console.log(num); let与var有所不同: 语法特点1:let变量只能在当 ...
- 本地计算机上的XXX服务启动后停止,某些服务在未由其它服务或程序使用时将自动停止
创建WindowsService,以及安装和卸载网上的资料一搜一大堆,在这里就不再做演示,只说明下博主在工作中使用WindowsService服务出现的错误,以及最终的结局方案. 1.启动window ...
- HttpClient filter中间转发从A tomcat转发至B tomcat
BackFilter.java 主要解决基于HttpGet/HttpPost以及基于HttpPost的附件流转发import java.io.IOException; import java.io.I ...
- 【JS】JavaScript中的闭包
在JavaScript中,闭包指的是有权访问另一个函数作用域中的变量的函数:创建闭包最常见的方式就是在一个函数内创建另一个函数.如下例子: function A(propertyName){ retu ...
- 1592: [Usaco2008 Feb]Making the Grade 路面修整
1592: [Usaco2008 Feb]Making the Grade 路面修整 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 428 Solv ...
- JavaWeb之Ajax
一.什么是Ajax 1.1.Ajax的定义 Ajax:(Asynchronous JavaScript And XML)指异步 JavaScript 及 XML 不是一种新的编程语言,而是一种用于创建 ...
- Java 中的锁——Lock接口
Java SE5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁功能.虽然它少了(通过synchronized块或者方法所提供的)隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的操作性. ...