Swift 单体仓库:iOS 与 macOS 代码组织方案

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

介绍:为什么在 Swift 中使用 Monorepo?



iOS 和 macOS 开发经常面临一个难题:如何组织代码,使其保持可扩展、可复用且易于维护?传统的多仓库方法(multirepo)会导致碎片化、依赖重复以及协调变更的复杂性。解决方案是 monorepo。



Monorepo 是一个包含项目所有组件的单一仓库:应用程序、库、构建工具和配置。对于 Swift 生态系统,这种方法尤其相关,因为 Swift Package Manager (SPM) 从 5.3+ 版本开始就很好地支持本地包和模块化架构。



在本文中,我们将探讨如何为 Swift 项目组织 monorepo,介绍结构、工具(Tuist, XcodeGen)以及带有代码示例的最佳实践。



1. Monorepo 对 Swift 的优势



在进入实现之前,让我们先看看关键优势:



  • 统一的依赖版本 — 所有模块使用相同版本的库,消除冲突。
  • 原子提交 — 一个包中的 API 更改会立即反映在所有依赖模块上,无需在仓库之间同步。
  • 简化 CI/CD — 一个流水线,一个配置,构建缓存。
  • 代码复用 — 通用工具、模型、网络层可轻松用于所有目标(iOS, macOS, watchOS)。
  • 代码审查 — 项目中不同部分的更改可以在一个 PR 中看到。


但也有缺点:仓库大小、克隆时间、需要精心设计的结构。但对于中等规模及以上的团队来说,monorepo 通常是最佳选择。



2. Swift Monorepo 的结构



考虑一个包含 iOS 应用和 macOS 应用以及共享库的项目的典型结构:



MyProject/├── Apps/│   ├── iOSApp/│   │   ├── Sources/│   │   │   └── AppDelegate.swift│   │   ├── Resources/│   │   └── Package.swift│   └── macOSApp/│       ├── Sources/│       │   └── AppDelegate.swift│       ├── Resources/│       └── Package.swift├── Packages/│   ├── Core/│   │   ├── Sources/│   │   │   ├── Core.swift│   │   │   └── Extensions/│   │   ├── Tests/│   │   └── Package.swift│   ├── Networking/│   │   ├── Sources/│   │   │   └── NetworkManager.swift│   │   ├── Tests/│   │   └── Package.swift│   └── UIComponents/│       ├── Sources/│       │   └── CustomButton.swift│       ├── Tests/│       └── Package.swift├── Tools/│   └── Scripts/│       └── build.sh├── .github/│   └── workflows/│       └── ci.yml├── Package.swift (根目录)├── Tuist.swift (如果使用 Tuist)└── README.md


结构说明:

  • Apps/ — 存放最终应用程序的目录。每个应用都是一个独立的 Swift 包,依赖于 Packages/ 中的包。
  • Packages/ — 所有内部库和模块。每个包都有自己的 Package.swift,可以独立测试。
  • Tools/ — 构建脚本、linter、代码生成器。
  • 根目录下的 Package.swift(或 Tuist/XcodeGen 配置) — 描述 workspace 和包之间的依赖关系。


2.1 根目录 Package.swift 示例 (SPM)



// swift-tools-version:5.9import PackageDescription

let package = Package( name: "MyProject", platforms: [ .iOS(.v16), .macOS(.v13) ], products: [ .library(name: "Core", targets: ["Core"]), .library(name: "Networking", targets: ["Networking"]), .library(name: "UICompon

博客

书籍推荐