使用Swift的单一代码库:代码组织实践

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

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

开始课程

使用 Swift 的 Monorepo:实践中的代码组织



在现代 iOS 和 macOS 开发中,选择代码管理策略是影响可扩展性、交付速度和产品质量的关键因素之一。传统上,开发者使用多仓库(polyrepo),每个模块或服务都存在于独立的 Git 仓库中。然而,随着项目和团队的增长,越来越多的问题浮现:如何组织代码,使其保持完整性、可复用且易于维护?答案是——使用 Swift 的 Monorepo。在本文中,我们将探讨什么是 monorepo,为什么它适合 Swift 项目,以及如何使用 Swift Package Manager (SPM) 在实践中进行组织。



什么是 Monorepo,为什么在 Swift 中需要它?



Monorepo 是一种将所有项目代码(或多个相关项目)存储在一个仓库中的方法。与每个组件都隔离的 polyrepo 不同,monorepo 将库、应用程序、测试、配置和文档统一在单一版本控制下。



对于 Swift 开发者来说,monorepo 尤其重要,原因如下:

  • 统一的依赖版本: 避免了不同应用程序之间库版本不同步的问题。
  • 简化重构: 对公共模块的更改会立即对仓库内的所有使用者可见。
  • 快速集成: 新功能可以在单个 pull request 中进行测试和发布。
  • 与 SPM 兼容: Swift Package Manager 非常适合 monorepo,因为它支持本地包和相对路径。


Swift 项目的 Monorepo 结构



让我们看一个实际例子。假设我们有一个 iOS 应用程序(使用 SwiftUI)和一个 macOS 应用程序(使用 AppKit),它们共享公共模块:网络层、认证逻辑和 UI 组件。我们按如下方式组织 monorepo:



MyMonorepo/├── Package.swift├── Sources/│   ├── AppiOS/│   │   └── main.swift│   ├── AppMacOS/│   │   └── main.swift│   ├── CoreNetworking/│   │   ├── Sources/│   │   │   └── HTTPClient.swift│   │   └── Tests/│   ├── CoreAuth/│   │   ├── Sources/│   │   │   └── AuthService.swift│   │   └── Tests/│   └── UIComponents/│       ├── Sources/│       │   └── CustomButton.swift│       └── Tests/├── Tests/│   ├── AppiOSTests/│   └── AppMacOSTests/├── .gitignore└── README.md


关键文件是 Package.swift。它描述了所有模块及其依赖关系。该文件的示例如下:



// swift-tools-version:5.9import PackageDescription

let package = Package( name: "MyMonorepo", platforms: [ .iOS(.v17), .macOS(.v14) ], products: [ .executable(name: "AppiOS", targets: ["AppiOS"]), .executable(name: "AppMacOS", targets: ["AppMacOS"]), .library(name: "CoreNetworking", targets: ["CoreNetworking"]), .library(name: "CoreAuth", targets: ["CoreAuth"]), .library(name: "UIComponents", targets: ["UIComponents"]) ], dependencies: [ // 外部依赖(如果需要) // .package(url: "https://github.com/...", from: "1.0.0") ], targets: [ .executableTarget( name: "AppiOS", dependencies: ["CoreNetworking", "CoreAuth", "UIComponents"], path: "Sources/AppiOS" ), .executableTarget( name: "AppMacOS", dependencies: ["CoreNetworking", "CoreAuth", "UIComponents"], path: "Sources/AppMacOS" ), .target( name: "CoreNetworking", dependencies: [], path: "Sources/CoreNetworking/Sources" ), .target( name: "CoreAuth",

博客

书籍推荐