组件化架构设计
一、架构图
二、架构说明
1. 主工程
职责:
- 作为应用的入口,组装各个业务组件
- 通过 Podfile 从索引库拉取所需组件
- 通过服务组件调用业务组件展示首页
2. 组件索引库
作用:
- 统一管理所有组件的 podspec 文件
- 提供组件版本管理和发布能力
- 主项目和组件都通过索引库获取依赖
3. 基础组件层
职责:
- 提供公共资源:多语言文件、图片资源、视频资源等
- 封装第三方库:网络库、图片加载库等
- 提供基础工具类:日期处理、字符串处理等
4. 业务组件层
职责:
- App启动时自动注册组件服务
- 实现具体的业务功能
- 可以调用其他业务组件的服务
- 使用基础组件提供的资源
特点:
- 各业务组件相互独立,可并行开发
- 通过服务组件进行通信,不直接依赖
- 每个组件都可以独立测试和发布
5. 服务组件层
职责:
- 提供注册服务
- 定义各业务组件的协议和服务
各个组件的Github地址:
三、实现流程
1. 创建组件工程
├── 使用 pod lib create 创建组件模板
└── 设置组件的基础信息和依赖
2. 配置组件功能
├── FMModuleBase: 提供资源 Bundle 访问等基础功能
├── FMModuleCenter: 实现服务注册和发现
└── FMModuleA/B: 实现业务逻辑,通过协议提供服务
3. 创建索引库
├── 在 GitHub 创建索引库
└── 添加到本地 CocoaPods 仓库
4. 发布组件
├── 验证组件 (pod lib lint)
├── 推送代码到 GitHub 并打 tag
└── 推送 podspec 到索引库 (pod repo push)
5. 集成使用
├── 在主项目中配置 Podfile
└── 安装依赖 (pod install)
四、核心机制
1. 基础功能访问机制(以资源访问为例
基础组件通过 Bundle 扩展提供资源访问能力:
Bundle.base // 访问基础组件资源
其他组件通过导入 FMModuleBase 并使用 Bundle.base 来访问共享资源。
2. 服务注册与发现机制
服务组件采用协议驱动的方式实现组件间通信:
- 服务定义:在
FMModuleCenter中定义所有组件的服务协议 - 服务注册:各组件在启动时通过
FMModuleCenter.shared.register()注册服务实现 - 服务发现:组件通过
FMModuleCenter.shared.get()获取其他组件的服务
这种设计实现了:
- 解耦:组件间不直接依赖,通过协议通信
- 可测试性:可以轻松 mock 服务进行单元测试
- 灵活性:可以动态替换服务实现
3. 依赖管理机制
通过 CocoaPods 的依赖管理实现:
- 本地开发:使用
:path指定本地路径,方便调试 - 远程使用:通过索引库管理版本,支持版本控制
- 自动解析:CocoaPods 自动解析和安装依赖组件
版本管理策略
- 基础组件:稳定版本,变更较少,版本号如
0.0.1、0.1.0 - 服务组件:核心组件,版本号如
0.0.1、1.0.0 - 业务组件:频繁迭代,版本号如
0.0.1、0.1.0、1.0.0
每个组件独立版本控制,通过 podspec 中的 s.version 指定版本号,并在 GitHub 上打对应的 tag。
五、开发与发布流程
开发阶段:
- 使用本地路径 (
:path) 进行开发调试 - 各组件可独立开发,互不干扰
发布阶段:
- 验证组件:
pod lib lint --sources='FMModuleLibrary' - 推送代码:提交到 GitHub 并打 tag
- 推送索引:
pod repo push FMModuleLibrary
使用阶段:
- 在主项目 Podfile 中指定索引库源
- 通过
pod install安装组件
六、具体操作
1. 创建 GitHub 仓库
首先,我们需要在 GitHub 上创建以下仓库:
FMModuleBase- 基础组件库FMModuleCenter- 服务组件FMModuleA- 业务组件AFMModuleB- 业务组件BFMModuleLibrary- 组件索引库(用于存放所有组件的 podspec 文件)
创建完成后,使用 GitHub Desktop 或其他 Git 工具将这些仓库克隆到本地。本文示例的项目目录为:/Users/xxx/Documents/Modules
2. 创建组件工程
使用 pod lib create 命令创建组件工程模板。在项目目录下依次执行:
pod lib create FMModuleBase
pod lib create FMModuleCenter
pod lib create FMModuleA
pod lib create FMModuleB
执行命令后,CocoaPods 会询问一些问题,根据项目需求选择即可。
// 工程支持的平台
What platform do you want to use?? [ iOS / macOS ]
> iOS
// 开发语言环境
What language do you want to use?? [ Swift / ObjC ]
> Swift
// App工程入口
Would you like to include a demo application with your library? [ Yes / No ]
> Yes
// 测试框架
Which testing frameworks will you use? [ Quick / None ]
> None
// 测试UI
Would you like to do view based testing? [ Yes / No ]
> No
创建完成后,组件的目录结构如下:
Modules
- FMModuleA
- FMModuleA # 组件源码目录
- FMModuleA.podspec # 组件配置文件
- Example # 示例工程
创建完成后,将生成的组件工程移动到对应的 GitHub 仓库文件夹中。
3. 配置基础组件(FMModuleBase)
3.1 添加资源文件
将图片、视频等资源文件添加到:
FMModuleBase/FMModuleBase/Assets
3.2 声明资源 Bundle
为了让其他组件能够访问基础组件的资源,需要声明资源所在的 Bundle。在 FMModuleBase 中创建 Bundle 扩展:
public extension Foundation.Bundle {
static var base: Bundle = {
let bundle = Bundle(for: FMModuleBase.self)
let bundleURL = bundle.url(forResource: "FMModuleBaseBundle", withExtension: "bundle")
let destBundle = bundleURL.flatMap { Bundle(url: $0) }
return destBundle ?? Bundle.main
}()
}
这样,其他组件就可以通过 Bundle.base 来访问基础组件的资源了。
4. 配置服务组件(FMModuleCenter)
4.1 实现服务注册机制
服务组件负责管理组件间的服务注册和发现。首先定义服务注册协议:
public protocol FMModuleCenterProtocol {
/// 注册服务
static func registerService()
}
然后实现服务组件类:
/// 服务组件
public class FMModuleCenter {
@MainActor public static let shared = FMModuleCenter()
private var services: [String: Any] = [:]
private init() {}
/// 注册服务
public func register<Service>(_ type: Service.Type, serviceImpl: Service) {
let key = String(describing: type)
services[key] = serviceImpl
}
/// 获取组件
public func get<Service>(_ type: Service.Type) -> Service? {
let key = String(describing: type)
return services[key] as? Service
}
}
4.2 声明组件服务协议
在 FMModuleCenter 中声明所有组件提供的服务协议:
/// 组件A提供的服务
public protocol FMModuleAServiceProtocol {
func getAViewController() -> UIViewController
// 其他服务...
}
/// 组件B提供的服务
public protocol FMModuleBServiceProtocol {
func getBViewController() -> UIViewController
// 其他服务...
}
这样设计的好处是:
- 组件间通过协议解耦,不直接依赖具体实现
- 所有服务协议集中管理,便于维护
- 支持组件间的服务发现和调用
5. 配置业务组件(FMModuleA、FMModuleB)
5.1 注册组件服务
在业务组件中创建OC类,利用OC Runtime的类加载机制,实现自动注册组件,如注册组件A:
#import "FMModuleARegistrar.h"
#import "FMModuleA/FMModuleA-Swift.h"
@implementation ModuleARegistrar
+ (void)load {
[FMModuleA registerService];
}
@end
5.2 使用基础组件资源
在业务组件中,可以通过以下方式使用基础组件的资源:
import FMModuleBase
let image = UIImage(named: "2", in: Bundle.base, with: nil)
5.3 组件间服务调用
业务组件可以通过服务组件获取其他组件的服务:
import FMModuleCenter
// 获取组件B的服务
let vc = FMModuleCenter.shared.get(FMModuleBServiceProtocol.self)?.getBViewController()
5.4 配置 podspec 依赖
在业务组件的 podspec 文件中,需要声明依赖关系:
s.dependency 'FMModuleBase'
s.dependency 'FMModuleCenter'
6. 创建 GitHub 索引库
6.1 添加索引库到本地
索引库用于存放所有组件的 podspec 文件。首先将索引库添加到本地:
pod repo add FMModuleLibrary https://github.com/MrLfm/FMModuleLibrary.git
添加成功后,索引库会保存在本地路径:~/.cocoapods/repos/FMModuleLibrary
7. 验证和推送组件到索引库
7.1 验证基础组件
在推送组件到索引库之前,需要先验证组件的正确性。验证基础组件:
pod lib lint FMModuleBase.podspec --allow-warnings
验证通过示例:
-> FMModuleBase (0.0.1)
- NOTE | xcodebuild: note: Using codesigning identity override: -
- NOTE | [iOS] xcodebuild: note: Building targets in dependency order
...
FMModuleBase passed validation.
7.2 推送基础组件到索引库
验证通过后,将基础组件代码推送到 GitHub,并打上版本标签(如 0.0.1):
git tag 0.0.1
git push origin 0.0.1
然后将组件推送到索引库:
pod repo push FMModuleLibrary FMModuleBase.podspec --allow-warnings
推送成功示例:
Validating spec
-> FMModuleBase (0.0.1)
...
Updating the `FMModuleLibrary' repo
Adding the spec to the `FMModuleLibrary' repo
- [Add] FMModuleBase (0.0.1)
Pushing the `FMModuleLibrary' repo
7.3 验证业务组件(遇到问题)
验证组件A时:
pod lib lint FMModuleA.podspec --allow-warnings
问题1:找不到依赖组件
报错信息:
-> FMModuleA (0.0.1)
- ERROR | [iOS] unknown: Encountered an unknown error (Unable to find a specification for `FMModuleBase` depended upon by `FMModuleA`
You have either:
* out-of-date source repos which you can update with `pod repo update` or with `pod install --repo-update`.
* mistyped the name or version.
* not added the source repo that hosts the Podspec to your Podfile.
) during validation.
[!] FMModuleA did not pass validation, due to 1 error.
原因分析:
组件A的 podspec 文件中声明了依赖 FMModuleBase 和 FMModuleCenter,但是验证命令默认只在 CocoaPods 的官方源中查找这些组件,而我们的组件是存放在自定义索引库中的。
解决方案:
在验证命令中指定索引库来源:
pod lib lint FMModuleA.podspec --allow-warnings --sources='FMModuleLibrary'
验证通过示例:
-> FMModuleA (0.0.1)
- NOTE | xcodebuild: note: Using codesigning identity override: -
...
FMModuleA passed validation.
7.4 推送业务组件到索引库
推送组件A到索引库时,同样需要指定索引库来源:
pod repo push FMModuleLibrary FMModuleA.podspec --allow-warnings --sources='FMModuleLibrary'
问题2:找不到 Git 标签
如果推送时遇到以下错误:
Validating spec
-> FMModuleA (0.0.1)
- ERROR | [iOS] unknown: Encountered an unknown error ([!] /usr/bin/git clone https://github.com/MrLfm/FMModuleA.git /var/folders/.../d20251120-43437-58g7ow --template= --single-branch --depth 1 --branch 0.0.1
Cloning into '/var/folders/.../d20251120-43437-58g7ow'...
warning: Could not find remote branch 0.0.1 to clone.
fatal: Remote branch 0.0.1 not found in upstream origin
) during validation.
[!] The `FMModuleA.podspec` specification does not validate.
原因分析:
CocoaPods 在验证组件时,会尝试从 GitHub 克隆对应版本的代码。如果 GitHub 仓库中没有对应的版本标签,验证就会失败。
解决方案:
- 将组件代码推送到 GitHub
- 在 GitHub 上创建版本标签(tag)
0.0.1,或者使用命令行:
git tag 0.0.1
git push origin 0.0.1
然后再执行推送命令。
7.5 推送其他组件
按照同样的方法,将 FMModuleCenter 和 FMModuleB 也推送到索引库。
8. 创建主工程项目并集成组件
8.1 创建主工程项目
创建一个新的 iOS 项目 FMApp,作为主项目。
8.2 配置 Podfile
在项目根目录创建 Podfile 文件,内容如下:
source 'https://github.com/MrLfm/FMModuleLibrary'
platform :ios, '13.0'
use_frameworks!
target 'FMApp' do
pod 'FMModuleA'
pod 'FMModuleB'
end
说明:
source指定了我们的自定义索引库地址- 只需要引入业务组件
FMModuleA和FMModuleB,它们的依赖(FMModuleBase和FMModuleCenter)会自动被引入
8.3 安装依赖
执行安装命令:
pod install
七、开发阶段的本地调试
在开发阶段,如果需要在主项目中直接使用本地组件进行调试,可以在 Podfile 中使用 :path 指定本地路径:
platform :ios, '13.0'
use_frameworks!
target 'FMApp' do
pod 'FMModuleA', :path => '../FMModuleA'
pod 'FMModuleB', :path => '../FMModuleB'
pod 'FMModuleBase', :path => '../FMModuleBase'
pod 'FMModuleCenter', :path => '../FMModuleCenter'
end
这样修改后,执行 pod install 即可使用本地组件进行开发调试。
八、总结
本文详细介绍了一个基于 GitHub 和 CocoaPods 的 iOS 组件化方案,主要包括:
- 组件创建:使用
pod lib create创建组件工程 - 组件配置:
- 基础组件提供公共资源和工具
- 服务组件管理服务注册和发现
- 业务组件实现具体功能
- 组件发布:通过索引库管理组件的 podspec 文件
- 问题解决:
- 验证组件时需要指定索引库来源
- 推送组件前需要确保 GitHub 上有对应的版本标签
- 项目集成:在主项目中通过 Podfile 引入组件
组件化的优势
- 代码解耦:组件间通过协议通信,降低耦合度
- 独立开发:不同团队可以并行开发不同组件
- 版本管理:每个组件可以独立版本控制
- 复用性:组件可以在多个项目中复用
注意事项
- 推送组件到索引库前,务必先验证组件
- 确保 GitHub 仓库中有对应的版本标签
- 验证和推送依赖组件时,需要指定索引库来源
- 组件间的依赖关系要合理设计,避免循环依赖
主工程项目运行示例: