从 07 年到现在,iOS 开发也近乎 10 年的时间了,随着 iOS 版本的一步步更新, Xcode 慢慢的稳定,社区的力量也随之变的越来越大,有很多工具可以帮助我们快速去搭建一个 App。

但是 一个 App 想要稳扎稳打,不畏惧任何版本迭代和业务变化,那么需要做的事情还是很多的,良好的 App 架构和前期的代码规范,以及效率工具的使用会使得整个 App 的迭代变的有条不紊,这也避免了后续大的迭代和更新的时候导致代码变的散乱。接下来我简单介绍一下这方面的内容,下面是一个脑图,里面的内容我将一一展开解释。

前期准备

代码规范

代码规范的重要性是重中之重,这也是笔者最为看中的一点。这里有 Google Objective-C Style GuideRaywenderlich Objective-C Style Guide 可以参考。这点从项目 kick off 的时候最好就规范起来。随着团队的扩大,新入的成员一定要严格按照团队的代码规范去编码,不然每个人的风格迥异,命名和代码格式都不一样,在接手项目或者迭代业务的时候会非常诡异,特别是那些具有强迫症的同学。还可以建设一个 Code Rivew 机制,指定一些红线标准来一步步落地。切记:能跑起来不是目的

Git 代码管理

  1. 这里推荐使用 Git 来管理自己的项目,可以使用 Github 的 private repo(当然是收费的咯),还可以自己在服务器上搭建 Gitlab 服务器来管理自己的代码。
  2. 平时编码的时候可以使用 SourceTree 这个软件来进行代码的 push, commit, merge 等操作。当然了,笔者还是习惯使用命令行来做这些事情,因为 SourceTree 偶尔也会抽风,已经不只一次的出现过了。
  3. Git-flow 的工作流程,这篇文章详细的说明的 git-flow 的使用方法。在多人团队开发的时候非常受益!创建自己的 master, develop, feature, release 等代码分支,以便可以在各个分支之间切换自如。
  4. 在 Git 提交时可以使用 [添加][修改][删除][修复][更新]等前缀词语来表明当前的 Commit 信息,规范化的 commit 信息能够一瞬间的告诉别人你干了些什么,防止自己忘记,避免团队成员看不懂。具体参见这里
  5. 代码 Review。可以使用 github 或 gitlab 自带的Merge Request 来进行代码 Review。同时还可以使用 Phacility,这里就不做过多的阐述。

Jenkins 持续集成

持续集成完美的解决了开发者的一个痛点,那就是避免 QA 同学频繁的找你打包。那么 Jenkins 将会是一个很好的选择,它可以同时支持 iOS 和 Android 平台。持续集成的复杂性也足够用几篇文章来描述了,Google 一下会有很多教程。这里介绍一篇博文供参考:手把手教你利用 Jenkins 持续集成 iOS 项目

Crash 收集平台

  1. iOS 日常的 Crash 问题收集我们可以采用一条龙服务的: Fabric腾讯 Bugly , 友盟,或者是开源系列的:KSCrash。可以自己搭建一套收集 Crash 的平台来满足日常的 Crash 收集问题。
  2. 苹果 Crash 收集服务。通过 iTunes Connect 获取用户的 Crash 日志。在 XCode 中 Window->Organizer->Crashes也可以看到同样的 crash 日志。收集 Crash 功能需要用户设置->隐私->诊断与用量->诊断与用量数据选择自动发送,并与开发者共享即可。这个成本太高,不推荐使用。

中场实战

好了,至此前期准备内容做完后,可以创建自己的项目工程了,我们的工程中 Podfile 里应该有一定的内容了,什么??Pod 各种出问题?那么我们可以使用这篇教程来搭建 Pod。好了,接下来我介绍一些日常开发所需要的内容。

工程结构

现阶段的 iOS 工程基本可以分为 All-In-One(指的是业务代码) 和 模块化 这两种工程结构。小项目笔者认为暂时没有必要使用模块化,因为每次的 pod installpod update 成本其实也不低。对于 All-In-One 的项目工程,笔者更不建议把所有工具类,通用类等都放入到 .pch 文件里,因为这会使编译速度变慢,另外如果你的工程以后有可能 模块化,那这以后将是一个巨大的灾难。

脱离业务逻辑的工具类(Category)

工具类能够帮助我们快速的实现我们想要的内容,并且保证程序的健壮性和代码的全局性,比如我们常见的 NSArray的安全访问或安全添加所有数据的方法,这个方法可以有效的抑制我们所出现的数组越界或者添加了一个 nil 对象所引起的闪退问题。

example
  • objc
1
2
3
4
5
6
7
8
9
10
11
12
13
- (id)ts_objectAtIndex:(NSUInteger) index{
if (index < self.count) {
return self[index];
} else {
return nil;
}
}

- (void)ts_addObject:(id)value {
if (value != nil) {
[self addObject:value];
}
}

诸如上面对 iOS 中 Foundation 和 UIKit 的扩展和工具类有很多,可以选用 YYCategories 或者 JKCategories 来作为我们的工具类,并且最好以 CocoaPods 的形式引入。并且在代码 Review 的时候注意开发者的使用是否合理和规范,避免重复造轮子。

第三方库的使用

第三方库能够帮你大大的提升开发效率,比如 Objective-C 中著名的 AFNetworkingSDWebimage,Swift 中著名的 AlamofireKingfisher 等开源库。那么我的建议是这些脱离业务逻辑的库最好不要拿来直接用,我们可以对它进行自己的封装。比如 YTKNetwork 就是对 AFNetworking 的一个很好的封装。当然了如果你的 App 没有那么的复杂,其实也可以自己简单的封装一下。

example
  • objc
1
2
3
4
5
6
7
8
9
#import "UIImageView+WebCache.h"

@implementation UIImageView (TSWebCache)

//首页列表的占位图
- (void)ts_setImageWithURLAtHomePage:(nullable NSURL *)url {
//这里还可以进行域名收敛等操作
[self sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"home_placeholder"]];
}

UI 实现

团队开发,笔者建议尽量使用代码的方式来进行 UI 布局。如果对滚动帧率和性能有极高的要求,可以选用 AutoresizingMask,具体可以看这篇文章的描述。但是如果需要适配 iPad,或者有转屏的需求,那么还是建议使用 AutoLayout 来处理,可以使用 Masonry 这个框架来实现。总之无论是 AutoresizingMask 还是 AutoLayout,只要团队统一就好,这也利于日后的维护和更新。

设计规范

对于 App 中每个字体的大小,颜色等内容我们可以定义一套 UIFontUIColor 的扩展来实现,并且我们可以和设计师沟通来使用字体编号,颜色编号进行标注。下图就是展示这种代码式的标注:

另外对于切图的内容,最好 2x 和 3x 的图分开切,每个图片我们还可以使用 TinyPNG 来进行一次压缩,这样也可以省一部分的 App 体积。

统计埋点

移动互联网的时代,大数据的舞台,埋点也是相当重要的,良好的的埋点 API 设计对代码的解耦也是非常有利的。

  1. 我们可以选用 腾讯 MTA 数据统计 这种第三方统计服务。
  2. 由于隐私性,我们也可以自己来实现一套埋点方案和设计。如果没有选用 MTA 服务,那么笔者建议下载 iOS 的 MTA 框架 并参考头文件中的内容进行自己 API 的设计。切记要高度解耦易扩展,即便是上下文的内容,也可以使用参数的形式来传递。
  3. 无痕埋点,iOS 有 Aspects 这种 AOP 的框架可以实现无痕埋点,比如日常的 ViewController 的生命周期,用户流转路径等等。当然了,如果是点对点的数据统计,还是建议我们使用上述的内容来完成这样的需求。

配置中心

国内 App 的动态化需求还是非常旺盛的,比如我们经常要根据服务器返回的数据进行 App 的动态调整。那么和服务器下发配置的时候,我们最好按照优先级且在不影响启动的情况下进行分布请求。比如电商系 App :

  1. 基础配置:一些基础的内容比如必须是第一阶梯的请求。例如:服务器时间AccessToken 等等。
  2. 大促:双 11 大促来了,首页要有个弹窗之类的逻辑控制,也会是服务器下发的配置。那么诸如此类的可以放到第二阶梯的请求中。如果服务端同学强烈要求放在第一阶梯的话,我们需要跟他们讲清楚启动时间对用户体验的重要性。
  3. 安全性:无论是什么配置,我们都需要进行安全性和准确性的校验,所以在这类的请求中。我们需要一套完善的机制来防止出现各种异常,对于这些配置的下发,App 要有顽强的容错能力。
  4. 皮肤,主题文件:很多 App 都会有夜间模式,换肤等功能。比如手机 QQ,手机淘宝等。那么类似这种换肤的功能,笔者建议可以使用 .zip 包的形式来进行资源整合。因为一套皮肤的图片可能会比较多,另外加上各种 2G, 3G 等异常网络情况的出现,客户端批量下载图片进行完整性和准确性的校验成本会大大增加。但是一个 .zip 包文件既可以解决资源是否下载完毕的问题,又可以一瞬间切换主题,用户几乎是 0 感知,体验也会非常赞。

Mock 数据

假数据的制造方法有很多种,笔者认为最妙的莫过于是使用 Json-server 来搭建的服务。我们可以把它部署到服务器,并且只要上传一段 json 文件,就可以返回你想要的数据,这也不影响你的开发进度,而且让这个过程变的很容易。

单元测试

一般小项目几乎很少会写单元测试的,因为在当前这个追求快速迭代和堆积业务的环境中,单元测试各种被无视。笔者认为非常重要的业务逻辑可以对数据等内容做单元测试,我们可以使用 Xcode 自带的测试框架 XCTest 来进行。但是当我们实现时候,不可避免的要去尽可能少的实例化一些具体的组件来保持测试既短又快,但现在的开发中,测试的组件很可能会有几个依赖的对象,我们需要用创造一些假的 Object 来替代实例化具体的依赖 Class,这时候你可能会使用到 OCMock 这个框架了。这样也可以快速帮你验证自己的业务逻辑。

通用的 WebViewController

移动开发的领域难免需要使用 WebView 搞一些事情。那么 JavaScript 和 native 的交互就显得十分有必要了。你可以使用 WebViewJavascriptBridge 来实现和 native 的交互。

持久化缓存

说到缓存,日常开发中我们基本都是在操作内存。像诸如图片这种,SDWebimage 已经帮我们实现了。那么刚才提到服务器下发的配置,还有首页的内容,笔者认为还是有必要缓存一份在本地,为什么呢?因为 App 启动无网络化是强烈依赖缓存的。无网络化可以大大提升启动效率,并且首页是有内容可以见的,这并不影响用户体验。iOS 的本地持久化方案有很多种,有直接写文件,NSKeyedArchiver 归档,还可以用 NSUserDefault,FMDBLevelDBRealm 等等。选择适合自己的就可以,记住 database 一定要跟业务解除绑定,它仅仅是为上层 UI 提供数据而已。

服务端异常 Log 上报

很多时候我们会遇见一些因为服务端数据所产生的业务逻辑的问题。这时候运营或者测试同学可能第一个找的就是客户端研发,因为表现层是在客户端的(T.T)。针对这个问题,笔者认为客户端和服务端返回的各种数据异常都应该有一个异常上报的过程。比如我们可以通过如下格式来进行上报:

example
  • json
1
2
3
4
5
6
{
"userId":"用户的 id",
"request_url":"http://xxx.com?a=1&b=2", //http 请求的 URL
"request_id":"http_1111", //http 请求的 id,可以给每个 id 进行编号,这样后台可以快速定位到是否是数据问题。
"error_message":"错误消息",
}

如果以上内容上报到服务器,那么运营同学和测试同学就可以第一时间排查是否是数据问题引起的用户界面异常。那么可能你会说:运营和测试怎么知道他们的 userId 呢,别着急,往下看彩蛋的内容。

调试彩蛋

不知道你是否记得新浪微博有一个线上版本把调试选项露出来了。如下图所示:

功能非常丰富,开发者的调试利器。其实上述的内容我们就可以指定在一个特殊的网络环境打开(比如 WiFi 名称为 “yourcompany-inc”),或者在 debug 模式下打开。除了上面的内容,我们还可以设置本地数据查看删除缓存并发请求测试触发内存警告流量统计网络监听FPS 页面监听 等功能。这部分内容可以大大的促进日常开发和 debug 的效率。

A/B Test

良好的数据埋点和 A/B Test 一结合那么就是现在流行的 Grouth Hacking 的基础,并且可以达到 灰度发布方案可逆数据驱动 的目的。

开花结果

经过了半个月的迭代,好了。App 终于有了它的雏形,那么后期就等着 App 顺利上线了,但是别忘了还有下列事情需要做的:

Bug 统计平台

测试和研发应该是患难兄弟,有 bug 了我们统计到一个平台,方便以后的复盘和总结。你可以使用 Teambition 或者 Tower 来到达这个目的。当然了,重要的是在以后的迭代过程中避免栽倒在一个地方。

App 签名和自动上传

前面的准备工作提到过利用 Jenkins 这个神器来进行持续集成的工作,那么我们同时需要在这个平台集成自己的 App 重签名和上传等机制。我们可以使用 Xcodebuild 脚本或者 Fastline 来实现。当然了,我们还可以选择使用 Fir.im蒲公英 这样的应用托管服务。

灰度发布

iOS 中的灰度发布比较受限制。厂内灰度可以使用 TestFlight 来做,或者征集一批 App 的忠实使用者,可以达到 2000 名的灰度效果,这部分用户可能只是在帮助我们测试是否有严重的 bug 等。但是有些功能我们还是需要发布到 App Store 后进行灰度的,这种情况下我们只能根据配置来进行了。

好了,以上内容基本是一个 App 所需要做的事情。每个节点的内容都可以深挖许多内容,笔者没有做过多的解释。如有出入,还忘指出。

Enjoy