iOS应用模块化的思考及落地方案(二)模块化自动构建工具的使用

954 阅读7分钟

1.0 iOS模块化中的问题

前文已经介绍了模块化的流程及一些常见的问题,我们在这里再次总结一下。

在工作中,当我们开始一个新项目的时候,最先考虑的就是模块化工作。

模块化工作的想法是很美好的,可是执行过程中会遇到很多的问题,而这些问题可能会让我们在工作中举步维艰。

  1. 工具使用问题。iOS的模块化一般会使用cocoapods工具,这个工具很强大,内容也很丰富,我们想要完成模块化工作,需要建立私有库,编写podspec文件,处理资源,编写Podfile文件,建立本地依赖等等。让团队成员每个人都精通这个工具是不必要的。因此经常会在使用工具中遇到不易解决的问题,从而浪费大量的时间。

  2. xcode设置问题。xcode设置项多如牛毛,很多内容看起来并不直观,需要我们去查阅官方文档来解决。而且这些设置数量多,使用的频率又少,所以难免会出现这样的情况:每个人都遇到的问题,然后各自去花时间解决,然后过段时间遇到相同的问题常常就忘记了,还要花时间去查阅解决,造成资源的重复浪费。

  3. 模块间依赖的问题,当你依赖的也是私有库的其他模块(下文中称为自有模块)时,开发中可能要同时修改多个模块,这样就会出现在多个工程中切换的问题。

  4. 规范问题,每个人建立模块的方式可能都不同,包括工程结构,工程设置等等。这样一来,不同的模块可能差异特别大,当跨模块开发或者代码交接的时候,可能就会出现难以解决的问题。

  5. 设置的变更修改都是手动修改,有时候难免会因为疏忽,而导致难以发现的错误,当需要处理的模块和依赖较多时,发生错误的概率也会增加。

2.0 编写自动化工具

为了解决这些问题,让团队能够将精力全部集中到业务开发中,特使用bash shell开发一个构建工具,用于自动化处理模块化的过程中遇到的设置及工具使用问题。

工具的地址如下:github.com/hardman/AWM…

使用这个自动化工具你将会获得如下能力:

  1. 一条命令即可创建模块工程,创建.podspec及Podfile文件,自动安装依赖,工程默认使用静态库,支持Swift和OC
  2. 一条命令即可拉取之前开发的模块,并且安装好所有依赖
  3. 一条命令即可自动打tag,自动更新.podspec文件,将工程推送到pod服务器
  4. 自有模块的列表,将会保存在单独git库中,便于有依赖模块时,可动态加载
  5. 自有模块的依赖都通过.podspec文件使用local path的方式安装,这样当被依赖的模块也需要修改时,不需要打开多个工程

因此,使用这个自动化工具,你不需要了解cocoapods工具,也不需要处理任何工程和工具设置,可以将注意力都集中到业务开发中。

【注】工具使用静态库作为模块的输出文件。

3.0 工具的使用

3.1 基本使用步骤

  1. 将工程clone到本地目录
  2. 打开tools/config修改配置文件
    • modulelistgitaddress.txt:新建一个git库,并将地址保存在这个文件中,地址最好是以git@开头。这个git库用于保存所有自有模块名称及地址。
    • podspecsaddr.txt:再新建一个git库,将地址保存在这个文件中,地址最好是git@开头。这个git库就是你的私有库地址。
    • podspecsname.txt:为你的git库取一个名字,保存在这个文件中
    • 上述3个文件都只有一行
    • dependencypodrepos.txt:这个文件保存你的app依赖的其他pod repos,一般情况下保持默认即可,支持多行,每行保存一个地址
    • 由于这些配置几乎不会修改,考虑将这些文件提交到你自己的git库中
  3. 执行 ./create.sh -n=[模块名] -b=[bundle id] -t=[s|f|r] 即可创建工程
    • 脚本执行过程中会要求输入一些工程基本信息及所依赖的模块,请认真输入,不要遗漏
    • 创建的工程会自动打开,并且可以直接执行
    • 创建好的模块文件在:工程根目录/modules
    • 例:./create.sh -n=HelloWorld -b=com.helloworld -t=s
  4. 模块开发完毕,需要将代码提交到develop分支,然后执行./push.sh [模块名] [tag]
    • 执行push.sh时,模块必须在develop分支上
    • 执行成功后,你的模块就已经提交成功,可以通过Podfile文件引用了
    • 例:./push.sh HelloWorld 0.0.1
  5. 使用 ./pull.sh [模块名] 即可下载其他未同步到本地的自有模块
    • 执行成功后,会自动下载所有依赖的模块,并通过local path添加到模块依赖中
    • 例:./pull.sh HelloWorld

3.2 如何提升模块所依赖其他模块的版本号

有的时候,当前模块所依赖的模块版本升级了,需要修改当前模块的依赖文件。 有2种方法:

  1. 直接修改文件
    • 需要修改的文件有2个,一个是文件根目录的dependency.txt文件,文件内记录了当前模块依赖的自有模块
    • dependency.txt文件记录模块版本的格式是:每行一个模块;格式为:模块名@@版本号,版本号支持~>前缀,不可带空格
    • 另一个文件是模块名.podspec文件
    • 按照podspec文件要求的格式去修改版本号
  2. 使用脚本修改
    • 执行命令:./utils.sh [模块名] upgradedependency [依赖的模块名] [版本号]
    • 其中版本号可以为空
    • 例子:./utils.sh LoginModule upgradedependency AFNetworking 3.5.0
    • 例子:./utils.sh LoginModule upgradedependency AFNetworking
  3. 版本号可支持英文字符:a.b.c

3.3 如何在模块工程内使用Swift&OC混编

  1. 在module工程内创建OC的类文件及swift文件,假设OC类名为 TestOC,swift类名为TestSwift
  2. 让OC能够访问Swift类
    • 只需要在TestOC.m中添加import。例:#import "模块名/模块名-Swift.h"
    • 另外需注意的是,TestSwift类必须是public并继承自NSObject。
  3. 让Swift能够访问OC类
    • 在 [模块名].h 这个文件中引入你的头文件。例:#import "TestOC.h"
    • xcode - build phases - [模块名].h 文件必须在public区域

3.4 关于模块资源文件的获取

  1. 由于模块都是静态库,所以最终运行到app中后,每个模块的资源文件(.xcassets, .xib, .png, .jpg, .jpeg, .gif, .txt, .plist, .bundle, .zip, .car)都是放到: "模块名.bundle"文件中的,而这个bundle在main bundle的根目录(这也是要求模块名防止重名的原因之一)
  2. 所以获取图片可以使用 UIImage.init(named: name, in: bundle, compatibleWith: nil) 方法
  3. 获取其他文件也需要指定bundle才可以
  4. 开发过程中,获取任何资源都需要带bundle,不能直接使用类似 UIImage.init(named:String)这种方法,即使是在模块工程内部的代码也不行
  5. 需要注意的是,静态库的单元测试target是无法获取资源的

3.5 注意事项

  1. 模块名要防止重复,不但要防止同一个私有库重复,也要防止与其他pod repo内的模块重复
  2. 依赖库不可产生循环依赖,比如 A依赖B,B依赖C,C依赖A
  3. 每个模块都有一个develop分支,develop分支的代码总是与最新的tag保持一致。执行push命令时,代码总是在develop分支上
  4. 开发期间(集成测试前)总是依赖本地模块,每次总是在集成测试前,才会执行push.sh脚本
  5. 如果当前的开发模块有修改,同时依赖的模块也有修改,则需要先push当前模块所依赖的模块,最后push当前模块。这时候可能需要使用 ./utils.sh [模块名] upgradedependency [依赖的模块名] [版本号] 命令修改模块所依赖的模块的版本号

--完--

  1. iOS应用模块化的思考及落地方案(一)模块的划分及模块化工作流程
  2. iOS应用模块化的思考及落地方案(二)模块化自动构建工具的使用