当工期比较紧的时候,项目开发中会经常出现移动端等待后端接口数据的情形,不但耽误项目进度,更让人有种无奈的绝望。所以在开发中,我们常常自己做些假数据,以方便开发和UI调试。然而做假数据方法不同,效率和安全性都各不同,有时稍有不慎,还会产生很大的bug。因此本文拟结合我在贝聊的开发经验,讲一讲我们组在iOS开发中曾经用过的做假数据的方法及其优劣。

示例项目

为方便下文的说明,本文主要以贝聊家长版app发现首页的热门帖子列表的实现为例。热门帖子列表的样式如下图:

这是比较常见的列表,用常用的UITableView实现即可,但需要自定义一个的UITableViewCell的子类ExploreTableViewCell。项目中,ExploreTableViewCell并没有用xib实现(因为xib日后不好修改,且代码复用性差),而是通过SnapKit用纯代码布的局,具体的布局代码大致如下:

import UIKit
import SnapKit
class  ExploreTableViewCell: UITableViewCell {let thumbnailImageView: UIImageViewlet titleLabel: UILabellet avatarImageView: UIImageViewlet authorNameLabel: UILabellet viewCountLabel: UILabellet commentCountLabel: UILabel//...其他属性override init(style: UITableViewCellStyle, reuseIdentifier: String?) {//创建viewthumbnailImageView = UIImageView()titleLabel = UILabel()avatarImageView = UIImageView()authorNameLabel = UILabel()viewCountLabel = UILabel()commentCountLabel = UILabel()//...其他view的创建super.init(style: style, reuseIdentifier: reuseIdentifier)//设置viewtitleLabel.numberOfLines = 2titleLabel.textColor = UIColor.blacktitleLabel.snp.makeConstraints { (make) -> Void inmake.left.equalTo(thumbnailImageView.snp.right).offset(15)make.right.equalTo(contentView.snp.right)make.top.equalTo(contentView.snp.top)}//...其他view的设置}//...其他业务代码
}复制代码

源码中写死

源码中写死数据是最便捷的假数据做法,项目很赶时,为最快速的看到UI效果,一般都会采取这种假数据方式。比如在上述热门帖子列表示例项目中,为查看整个ExploreTableViewCell的布局效果,在titleLabelsubview的设置位置,直接写死假数据。

//...其他代码
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {//...其他初始化代码//写死的假数据代码titleLabel.text = "这是一个标题这是一个标题这是一个标题这是一个标题这"thumbnailImageView.image = UIImage(named:"sampleImage")avatarImageView.image = UIImage(named:"sampleImage")authorNameLabel.text = "作者名"viewCountLabel.text = "1000"commentCountLabel.text = "1000"//...其他初始化代码}
//...其他代码复制代码

源码中写死假数据虽然方便,但稍有不慎就容易直接上线上环境(因为测试在测试时一般都会有数据,假数据被遮盖了),演变成一个有可能非常严重也有可能很轻的bug(贝聊就切实出现过这样的bug,而且还是个影响广泛的大bug),为安全起见,所有写死的假数据都应该包在条件编译宏内。

//...其他代码
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {//...其他初始化代码//写死的假数据代码,包裹在条件编译宏内#if DEBUGtitleLabel.text = "这是一个标题这是一个标题这是一个标题这是一个标题这"thumbnailImageView.image = UIImage(named:"sampleImage")avatarImageView.image = UIImage(named:"sampleImage")authorNameLabel.text = "作者名"viewCountLabel.text = "1000"commentCountLabel.text = "1000"#endif//...其他初始化代码}
//...其他代码复制代码

包在条件编译宏内,就可以保证不污染正式环境的代码,从而保证安全性。

利用单元测试的网络请求stub做假数据

在源码中写死假数据,有以下三个缺点,

  • 污染源代码
    假数据写在源代码中,即使用宏包裹起来,只是保证了一定的安全性,但依然污染了源代码,如果上线前忘了把假数据代码移除,它一直会残留在源代码中,而且还会一直影响DEBUG环境的调试。
  • 假数据散落四处,无法集中管理
    本文示例代码的假数据虽然写在一块,但在实际开发中,并不是所有的UI代码都在一个文件中。即使在一个文件内,往往各个属性的初始化和设置也不在一个方法内。代码一多,基本很难管理。
  • 扭曲了数据的正确流通
    正确的数据产生方式,应该是发一个网络请求,然后把请求回来的数据转成model,最后通过model给各个UI组件填充数据。而在源代码中写死假数据,直接打乱了数据的正确流通,这会使得整个开发的逻辑是颠倒的,不但使开发更容易出bug,而且逻辑流的切换带来的开发效率和开发感受都很差。

较好的假数据方式,应该尽可能的不污染源代码,不扰乱正常的数据流通,而且能集中管理。在研究单元测试时,我无意中发现stub某个页面请求数据的网络请求即可达到这种完美的假数据效果。

首先按正常的流程开发整个功能,(在开发中,我总是倾下于先创建Model,而不是先写UI)

  1. 创建Model
  2. 创建ViewController
  3. 创建View等UI元素
  4. 在ViewController中完成网络请求的发起,并完成从网络数据到Model的转换
  5. 应用Model填充UI

整个功能开发按照有真实网络请求进行,但事实上并没有网络请求,因为后台并未搭好,没关系,先按照后台给出的接口和数据格式定义,创建一个本地JSON文件。对于本文的示例(假定只有列表数据)来说,文件名暂为hotTopics.json,内容大致如下(贝聊发现首页实际上有很多其他元素,网络请求返回的JSON也比这个复杂的多):

{hotTopics: [{"title": "这是一个标题这是一个标题这是一个标题","thumbnail": "https://api.beiliao.com/explore/image/fdlafjlfp34523.jpg","author": "小黄老师","authorAvatar": "https://api.beiliao.com/explore/image/fdlafjlfp34523.jpg","commentCount": 1000,"viewCount": 3000},{"title": "这是另外一个标题这是另外一个标题这是另外一个标题","thumbnail": "https://api.beiliao.com/explore/image/fdla32131fjlfp34523.jpg","author": "小李老师","authorAvatar": "https://api.beiliao.com/explore/image/fdl232afjlfp34523.jpg","commentCount": 1030,"viewCount": 3400}]
}复制代码

然后在ViewControllerstubViewController中所有的网络请求,我在开发中用的是OHHTTPStubs,大致的代码如下:

class ExploreViewController: UITableViewController {//...其他代码override func viewDidLoad() {super.viewDidLoad()//...其他代码#if DEBUGstubRequests()#endif//...其他代码}//...其他代码func stubRequests() {stub(isPath("/explore/hotTopics")) { _ inlet stubPath = OHPathForFile("hotTopics.json", type(of: self))return fixture(filePath: stubPath!, headers: ["Content-Type":"application/json"])}}
}复制代码

注意所创建的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更新开始===============

文章发出后,不少读者反馈,

  1. Charles与Shadowsocks可以共存
    具体怎么共存,我还有待研究,后续文章再做补充
  2. 假数据的数据应该能随机生成
    具体由json server、mock.js和Charles三者结合完成,有意思,值得研究,待后续文章详细补充。

=============2017-03-04更新结束===============

总结

一路试下来,其实只有第一种源码中写死和最后一种网络代理两种假数据方式最常用,虽然第一种缺点最多,但方便快捷,最后一种虽无任何缺点,但启动还是有点麻烦。

写了这么多,还是希望对大家有所启发。


欢迎关注我的微博:轻墨lightink
文章同步发布在贝聊知乎

[贝聊科技]如何在iOS开发中更好的做假数据?相关推荐

  1. [贝聊科技]如何将 iOS 项目的编译速度提高5倍

    前言 贝聊目前开发的两款App分别是贝聊家长版和贝聊老师版,最近因为在快速迭代开发新功能,项目规模急速增长,单个端业务代码约23万行,私有库约6万行,第三方库代码约15万行,单个客户端的代码行数约60 ...

  2. [贝聊科技]在微信小程序中渲染HTML内容

    大部分Web应用的富文本内容都是以HTML字符串的形式存储的,通过HTML文档去展示HTML内容自然没有问题.但是,在微信小程序(下文简称为「小程序」)中,应当如何渲染这部分内容呢? 解决方案 wxP ...

  3. 如何在iOS应用中更好的调试H5页面

    前言 在APP中,H5页面可以通过加载vConsole脚本,来查看页面的日志.异常.网络加载.设备信息.储存信息.元素.但是,JS脚本加载于页面Dom挂载之后,这样就会使得这一区间的信息丢失,而且vC ...

  4. [贝聊科技]如何实现一个 AttributedLabel

    作者:陈浩 贝聊科技移动开发部 iOS 工程师 Core Text 是苹果提供的富文本排版技术,可以定制开发图文混排功能,DTCoreText.Nimbus.YYLabel 等优秀的开源库底层都是基于 ...

  5. [贝聊科技]贝聊 iPhone X 适配实战

    @NewPan 贝聊科技 iOS 菜鸟工程师 这款为天猫定制的 iPhone,你买了吗?由于没摸过真机,所以严格意义上来说,这篇文章应该有一个更加接地气的名字:"模拟器适配实战". ...

  6. iOS开发中的Web应用概述

    为了更好的阅读体验,建议阅读原文 插播广告 -- 几十行代码完成资讯类App多种形式内容页 HybridPageKit :一个针对资讯类App高性能.易扩展.组件化的通用内容页实现框架. 想和我一起全 ...

  7. [贝聊科技]贝聊 IAP 实战之订单绑定

    大家好,我是贝聊科技 的 iOS 工程师 @NewPan. 注意:文章中讨论的 IAP 是指使用苹果内购购买消耗性的项目. 这次为大家带来我司 IAP 的实现过程详解,鉴于支付功能的重要性以及复杂性, ...

  8. ios 动态化视图_如何在iOS应用中使高度收集视图动态化

    ios 动态化视图 by Payal Gupta 通过Payal Gupta 如何在iOS应用中使集合视图的高度动态化 (How to make height of collection views ...

  9. 教你如何在iOS项目中设置各种字体

    原文地址为: 教你如何在iOS项目中设置各种字体 在iOS开发中设置字体的方法有很多种,下面为大家介绍比较常用的三种方法 1.使用系统默认提供的字体 系统默认提供的字体主要是指UIFont中提供的字体 ...

最新文章

  1. Nginx配置段(3)
  2. Kafka常用命令大全
  3. 原生html小游戏,原生JS实现别踩白块小游戏(一)
  4. Js中数组去重的几种方法
  5. java保存文件到linux指定目录_怎么使用java编程实现linux下全部文件目录的遍历
  6. YEARWEEK函数来得到本周的日期
  7. 关于Servlet出现乱码问题
  8. vuex其实超简单,只需3步
  9. 反转链表python
  10. python绘制正态分布函数_Python数据清洗(三):异常值识别与处理
  11. SpringCloud工作笔记068---登录堡垒机_下载文件报错_Xshell向Linux上传下载文件_lrzsz_ZModem
  12. 三维曲面图像绘制(光照控制)
  13. 新能源汽车行业资讯-2022-9-13
  14. Clark变换与Park变换
  15. 强化学习10——迭代学习
  16. JavaScript中的随机数--随机点名器
  17. 简单记账本-android版
  18. 出了山寨机,国产的就没法活了?
  19. win10安装账户卡住_安装win10系统卡住不动的原因和处理方法
  20. 总体标准差、样本标准差、标准误差

热门文章

  1. 美国大学录取放榜日总结
  2. 移动应用开发常见问题
  3. HTML+CSS+JS实现 ❤️飞行人物图标动画特效❤️
  4. 接近淘宝 80%的大数据实时计算平台,从0搭建的经验和坑
  5. MySQL SQL更新锁定
  6. php代码如何使用,PHP如何使用strval()函数?用法和代码示例
  7. matplotlib横坐标从大到小_Python数据分析:Matplotlib
  8. H5跳转小程序,uniapp跳转小程序
  9. 老泪纵横!咱07年炒股居然还巨亏
  10. java面试题目(含解答)汇总