Monorepository mit Swift: Code-Organisation in der Praxis

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Monorepository mit Swift: Code-Organisation in der Praxis

\n\n

Die moderne iOS-Entwicklung steht zunehmend vor der Herausforderung der Skalierung von Codebasen. Wenn ein Projekt auf Dutzende von Modulen, gemeinsame Bibliotheken und mehrere Anwendungen (iOS, macOS, watchOS) anwächst, beginnt das traditionelle Modell mit separaten Repositories (Polyrepo) die Entwicklung zu verlangsamen. Abhilfe schafft das Monorepository – ein einziges Repository, das den Code aller Komponenten des Ökosystems enthält. In diesem Artikel werden wir praktisch erläutern, wie man ein Monorepository für Swift-Projekte mit modernen Tools organisiert: Swift Package Manager (SPM), Tuist und effiziente CI/CD-Pipelines.

\n\n

1. Warum ein Monorepository im Swift-Ökosystem?

\n\n

Bevor wir in die Umsetzung eintauchen, ist es wichtig, die wichtigsten Vorteile des Ansatzes zu verstehen:

\n\n
    \n
  • Einheitliche Versionierung – alle Module und Anwendungen befinden sich in einem Commit, was das Problem des "Abhängigkeits-Desasters" (dependency hell) ausschließt.
  • \n
  • Vereinfachtes Refactoring – eine API-Änderung in einer Bibliothek ist sofort für alle Konsumenten sichtbar; der Compiler hebt Fehler innerhalb eines PRs hervor.
  • \n
  • Transparente Navigation – Entwickler sehen den gesamten Code des Projekts, ohne zwischen Repositories wechseln zu müssen.
  • \n
  • Optimierung von CI/CD – es können nur geänderte Module gebaut werden (targeted builds), was Zeit und Ressourcen spart.
  • \n
\n\n

Ein Monorepository erfordert jedoch Disziplin: eine klare modulare Architektur und Werkzeuge, die Chaos verhindern. Für Swift ist die beste Wahl heute Tuist – ein Tool, das Xcode-Projekte deklarativ verwaltet, oder der reine Swift Package Manager für eine modulare Struktur.

\n\n

2. Struktur eines Monorepositorys mit Swift Package Manager

\n\n

Beginnen wir mit der grundlegenden Organisation. Angenommen, wir haben zwei Anwendungen: MyAppiOS und MyAppMacOS, sowie eine Reihe interner Bibliotheken. Die Dateistruktur könnte wie folgt aussehen:

\n\n
Monorepo/\n├── Apps/\n│   ├── MyAppiOS/\n│   │   ├── Sources/\n│   │   └── Package.swift (oder project.yml für Tuist)\n│   └── MyAppMacOS/\n│       ├── Sources/\n│       └── Package.swift\n├── Libraries/\n│   ├── Networking/\n│   │   ├── Sources/\n│   │   └── Package.swift\n│   ├── UIComponents/\n│   │   ├── Sources/\n│   │   └── Package.swift\n│   └── Core/\n│       ├── Sources/\n│       └── Package.swift\n├── Tests/\n│   ├── NetworkingTests/\n│   └── UIComponentsTests/\n├── Package.swift (root manifest)\n└── Tuist/ (falls wir Tuist verwenden)\n    └── Project.swift\n
\n\n

Das root Package.swift verbindet alles miteinander. Beispiel eines Manifests:

\n\n
// swift-tools-version:5.9\nimport PackageDescription\n\nlet package = Package(\n    name: "MyMonorepo",\n    platforms: [\n        .iOS(.v16), .macOS(.v13)\n    ],\n    products: [\n        .library(name: "Networking", targets: ["Networking"]),\n        .library(name: "UIComponents", targets: ["UIComponents"]),\n        .library(name: "Core", targets: ["Core"]),\n        .executable(name: "MyAppiOS", targets: ["MyAppiOS"]),\n        .executable(name: "MyAppMacOS", targets: ["MyAppMacOS"])\n    ],\n    dependencies: [\n        // Externe Abhängigkeiten\n        .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0")\n    ],\n    targets: [\n        .target(\n            name: "Networking",\n            dependencies: ["Core", .product(name: "Alamofire", package: "Alamofire")],\n            path: "Libraries/Networking/Sources"\n        ),\n        .target(\n            name: "UIComponents",\n            dependencies: ["Core"],\n            path: "Libraries/UIComponents/Sources"\n        ),\n        .target(\n            name: "Core",\n            path: "Libraries/Core/Sources"\n        ),\n        .executableTarget(\n            name: "MyAppiOS",\n            dependencies: ["Networking", "UIComponents"],\n            path: "Apps/MyAppiOS/Sources"\n                

Blogs

Buchempfehlungen