Swift 模块化项目结构最佳实践
原文链接:http://liuduo.me/2017/05/22/moduleproject/
微博:@刘铎Derek
这篇文章没有技术,只讲怎么用 Xcode 组织项目结构。
开篇
写了这么多年的代码,目前觉得最好的构造项目结构的方式就是 模块化
。
模块化在 Objective-C
的项目中可能比较难搞起来,但是在 Swift
项目中就游润有余了,因为 Swift
有着天生 Module
的概念,和它的访问控制修饰符。
小型项目搞模块化意义可能不大,对于中型和大型的项目,模块化以后,纵向分层横向分模块,模块间解耦,访问控制,依赖注入,依赖倒转,在开发过程中就能自然而然的写出来,省去以后重构的苦恼。
大家都知道,Xcode 主要是用 Workspace
和 Project
来组织项目结构的,一个工程只能有一个 Workspace
,一个 Workspace
中可以有任意多个 Project
。
而模块化的方式就是把各个业务功能拆分成模块(Framework),然后主工程负责把这些业务模块串联起来构成一个完成的 App。
工程结构
在一个项目里,有一个 Workspace
,其中有很多个 Project
,主工程是一个 Project
,每个业务模块也都是一个 Project
。
效果是这样的:
在工程中,每添加一个新模块的时候,通过点击左下角的 +
来创建,选择 Framework
类型:
路径选择到 Modules 目录下。
如果你觉得需要的话,可以对 Modules 目录下的这些 Project 再分目录。
如何创建 Workspace?
如果是从一个新工程开始创建,可以在 Xcode 中选择:File -> New -> Workspace...
创建,然后再在创建好的 Workspace 中添加主工程。
如果你是用了 CocoaPods,在第一次 pod install
时,CocoaPods 会帮你创建好 Workspace。
依赖配置
对于每一个模块 xcodeproj,都需要在这里严格配置好它的依赖。
同属于一个 Workspace
的 Project
们,可以方便的互相配置依赖,Xcode 可以很好的处理好这个依赖。
配置了依赖以后的 Framework
,在编译时 Xcode 会先编译它依赖的那些 Framework
, 确保这个 Framework
可以顺利配置成功。
用这种项目结构时,依赖必须严格配置正确,否则即使在模拟器或真机运行时没有问题,打包的时候一定会出现问题。这样当想知道一个模块依赖了哪些模块时,可以直观的从这里查看到。
为每一个模块添加 Demo 和单元测试
由于一个模块是一个单独的 Project
,可以在里面方便的添加 Demo 和单元测试的 Target。
点坐下的 +
即可添加。
CocoaPods
如果你的某一个模块需要依赖 CocoaPods
中的库,CocoaPods 本身也很好的支持了这个需求。
在 Podfile
文件中添加一个 target
的配置即可。
target 'Slide' do
project "Modules/Slide/Slide"
pod 'SDWebImage', '3.8.2'
pod 'SDWebImage/WebP'
end
Carthage
使用 Swift 语言的项目也会经常用到 Carthage
。一般都是直接依赖 Carthage/Build
中编译好的 .framework
文件,然后类型为 App 的 Project,要配置 copy-framework 脚本,类型为 Framework 的 Project,要在 Build Phases 中添加一个 Copy Files Phases,这是官方文档中的说法。
但是在我们这种项目结构中,如果为每个模块都配置 Copy Files Phases,会导致打出来的包中,每个模块的 Framework 都包含了它依赖的那些 Framework,导致包大小非常大,因为里面包含了非常多的重复 Framework。
于是我们再 Workspace 中又建立的一个目录叫 CarthageFrameworks
,然后把 Carthage/Checkout
目录中的那些 .xcodeproj 文件拖到这个 CarthageFrameworks 目录中,效果如下:
这样不仅解决了打包重复包含 Framework 的问题,还能方便的查看 Carthage 引入的库的代码,并且方便的设断点调试。
列举一下好处
- 模块化,各模块之间独立,职责清晰
- 开发过程中会自然强制你考虑模块间解耦,访问控制,依赖注入,依赖倒转等问题
- 依赖必须严格配置,方便查看和梳理依赖
- 每个
Project
都有一个xxx.xcodeproj
文件,这个文件是最常出现冲突的文件了,而且这个文件的冲突还往往很难解决,模块化分开后,各个模块都有单独的xxx.xcodeproj
文件,大大降低发生冲突的概率。 - 模块拥有自己的 Targets 空间,方便创建 Demo 和单元测试
- 如果想把模块做成单独的 git 仓库,也非常的方便
- 方便进行 Carthage 引入的库的调试