Monorepo avec Swift : organisation du code pour les projets iOS et macOS
Dans le développement moderne pour iOS et macOS, le choix de la stratégie de gestion du code influence directement la vitesse de développement, la qualité du refactoring et la facilité du travail en équipe. L'approche traditionnelle avec de nombreux dépôts séparés (polyrepo) conduit souvent à la duplication des dépendances, à des difficultés de synchronisation des versions et à une fragmentation de la base de code. Une alternative est le monorepo — un dépôt unique pour tous les modules, bibliothèques et applications du projet. Dans cet article, en prenant l'exemple de Swift et de l'écosystème Apple, nous verrons comment organiser un monorepo, quels outils utiliser et quels pièges prendre en compte.
Qu'est-ce qu'un monorepo et pourquoi en a-t-on besoin dans les projets Swift
Un monorepo est une stratégie de gestion de versions dans laquelle tout le code du projet (plusieurs applications, bibliothèques, configurations) est stocké dans un seul dépôt Git. Dans le contexte de Swift, cela signifie que vous pouvez avoir dans un seul dépôt :
- une application iOS (par exemple,
MyApp) - une application macOS (
MyMacApp) - une bibliothèque de modèles partagée (
CoreModels) - une couche réseau (
Networking) - des composants UI (
UIComponents) - des tests et des scripts CI
Avantages du monorepo pour les projets Swift :
- Version unique des dépendances — tous les modules utilisent la même version d'une bibliothèque (par exemple,
AlamofireouSwiftUI), ce qui élimine les conflits. - Commits atomiques — les modifications de l'API d'une bibliothèque et son utilisation dans l'application sont enregistrées en un seul commit, ce qui simplifie la revue de code et le rollback.
- Simplification du refactoring — vous pouvez renommer une méthode publique dans un module et corriger immédiatement tous les endroits où elle est appelée dans le même dépôt.
- CI/CD partagé — un seul pipeline pour la compilation et les tests de tous les composants.
Outils pour travailler avec un monorepo en Swift
Dans l'écosystème Apple, il existe deux outils principaux pour organiser un monorepo : Swift Package Manager (SPM) et Tuist. Examinons leurs caractéristiques.
Swift Package Manager (SPM)
SPM est un gestionnaire de paquets intégré qui convient parfaitement aux monorepos. Vous définissez les modules via Package.swift, et Xcode résout automatiquement les dépendances. Exemple de structure :
MyMonorepo/├── Package.swift├── Sources/│ ├── CoreModels/│ │ └── Models.swift│ ├── Networking/│ │ └── APIClient.swift│ └── UIComponents/│ └── ButtonView.swift├── Apps/│ ├── MyApp/│ │ └── (projet iOS)│ └── MyMacApp/│ └── (projet macOS)└── Tests/ ├── CoreModelsTests/ └── NetworkingTests/Exemple de Package.swift pour un tel monorepo :
// swift-tools-version:5.9import PackageDescription
let package = Package( name: "MyMonorepo", platforms: [ .iOS(.v16), .macOS(.v13) ], products: [ .library(name: "CoreModels", targets: ["CoreModels"]), .library(name: "Networking", targets: ["Networking"]), .library(name: "UIComponents", targets: ["UIComponents"]), ], dependencies: [ .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0") ], targets: [ .target(name: "CoreModels"), .target( name: "Networking", dependencies: ["CoreModels", "Alamofire"] ), .target( name: "UIComponents", dependencies: ["CoreModels"] ), .testTarget(name: "CoreModelsTests", dependencies: ["CoreModels"]), .testTarget(name: "NetworkingTests", dependencies: ["Networking"]), ])