iOS 组件化方案:GitHub+CocoaPods

101 阅读10分钟

组件化架构设计

一、架构图

截屏2025-11-21 上午10.16.40.png

二、架构说明

1. 主工程

职责:

  • 作为应用的入口,组装各个业务组件
  • 通过 Podfile 从索引库拉取所需组件
  • 通过服务组件调用业务组件展示首页

2. 组件索引库

作用:

  • 统一管理所有组件的 podspec 文件
  • 提供组件版本管理和发布能力
  • 主项目和组件都通过索引库获取依赖

3. 基础组件层

职责:

  • 提供公共资源:多语言文件、图片资源、视频资源等
  • 封装第三方库:网络库、图片加载库等
  • 提供基础工具类:日期处理、字符串处理等

4. 业务组件层

职责:

  • App启动时自动注册组件服务
  • 实现具体的业务功能
  • 可以调用其他业务组件的服务
  • 使用基础组件提供的资源

特点:

  • 各业务组件相互独立,可并行开发
  • 通过服务组件进行通信,不直接依赖
  • 每个组件都可以独立测试和发布

5. 服务组件层

职责:

  • 提供注册服务
  • 定义各业务组件的协议和服务

各个组件的Github地址:

主工程

组件索引库

基础组件

服务组件

业务A组件

业务B组件

三、实现流程

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.10.1.0
  • 服务组件:核心组件,版本号如 0.0.11.0.0
  • 业务组件:频繁迭代,版本号如 0.0.10.1.01.0.0

每个组件独立版本控制,通过 podspec 中的 s.version 指定版本号,并在 GitHub 上打对应的 tag。

五、开发与发布流程

开发阶段:

  • 使用本地路径 (:path) 进行开发调试
  • 各组件可独立开发,互不干扰

发布阶段:

  1. 验证组件:pod lib lint --sources='FMModuleLibrary'
  2. 推送代码:提交到 GitHub 并打 tag
  3. 推送索引:pod repo push FMModuleLibrary

使用阶段:

  • 在主项目 Podfile 中指定索引库源
  • 通过 pod install 安装组件

六、具体操作

1. 创建 GitHub 仓库

首先,我们需要在 GitHub 上创建以下仓库:

  • FMModuleBase - 基础组件库
  • FMModuleCenter - 服务组件
  • FMModuleA - 业务组件A
  • FMModuleB - 业务组件B
  • FMModuleLibrary - 组件索引库(用于存放所有组件的 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 文件中声明了依赖 FMModuleBaseFMModuleCenter,但是验证命令默认只在 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 仓库中没有对应的版本标签,验证就会失败。

解决方案:

  1. 将组件代码推送到 GitHub
  2. 在 GitHub 上创建版本标签(tag)0.0.1,或者使用命令行:
git tag 0.0.1
git push origin 0.0.1

然后再执行推送命令。

7.5 推送其他组件

按照同样的方法,将 FMModuleCenterFMModuleB 也推送到索引库。

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 指定了我们的自定义索引库地址
  • 只需要引入业务组件 FMModuleAFMModuleB,它们的依赖(FMModuleBaseFMModuleCenter)会自动被引入
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 组件化方案,主要包括:

  1. 组件创建:使用 pod lib create 创建组件工程
  2. 组件配置
    • 基础组件提供公共资源和工具
    • 服务组件管理服务注册和发现
    • 业务组件实现具体功能
  3. 组件发布:通过索引库管理组件的 podspec 文件
  4. 问题解决
    • 验证组件时需要指定索引库来源
    • 推送组件前需要确保 GitHub 上有对应的版本标签
  5. 项目集成:在主项目中通过 Podfile 引入组件

组件化的优势

  • 代码解耦:组件间通过协议通信,降低耦合度
  • 独立开发:不同团队可以并行开发不同组件
  • 版本管理:每个组件可以独立版本控制
  • 复用性:组件可以在多个项目中复用

注意事项

  1. 推送组件到索引库前,务必先验证组件
  2. 确保 GitHub 仓库中有对应的版本标签
  3. 验证和推送依赖组件时,需要指定索引库来源
  4. 组件间的依赖关系要合理设计,避免循环依赖

主工程项目运行示例:

未命名.gif

如果这篇文章对你有帮助,请点个赞吧👍🏻