Monorepositorio con Swift: organización de código en la práctica

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Monorepositorio con Swift: organización de código en la práctica

\n\n

El desarrollo moderno de iOS se enfrenta cada vez más al desafío de escalar la base de código. Cuando un proyecto crece hasta tener decenas de módulos, bibliotecas compartidas y varias aplicaciones (iOS, macOS, watchOS), el modelo tradicional con repositorios separados (polyrepo) comienza a ralentizar el desarrollo. En su ayuda viene el monorepositorio: un repositorio único que contiene el código de todos los componentes del ecosistema. En este artículo analizaremos en la práctica cómo organizar un monorepositorio para proyectos Swift, utilizando herramientas modernas: Swift Package Manager (SPM), Tuist y pipelines eficientes de CI/CD.

\n\n

1. ¿Por qué es necesario un monorepositorio en el ecosistema Swift?

\n\n

Antes de sumergirnos en la implementación, es importante entender las ventajas clave del enfoque:

\n\n
    \n
  • Versión única — todos los módulos y aplicaciones están en un mismo commit, lo que elimina el problema del "infierno de dependencias" (dependency hell).
  • \n
  • Refactorización simplificada — un cambio de API en una biblioteca se ve inmediatamente en todos los consumidores; el compilador resaltará los errores dentro de un mismo PR.
  • \n
  • Navegación transparente — los desarrolladores ven todo el código del proyecto sin tener que cambiar entre repositorios.
  • \n
  • Optimización de CI/CD — se puede configurar la compilación solo de los módulos modificados (targeted builds), ahorrando tiempo y recursos.
  • \n
\n\n

Sin embargo, un monorepositorio requiere disciplina: una arquitectura modular clara y herramientas que eviten el caos. Para Swift, la mejor opción hoy en día es Tuist, una utilidad que gestiona proyectos Xcode de forma declarativa, o el Swift Package Manager puro para una estructura modular.

\n\n

2. Estructura de un monorepositorio con Swift Package Manager

\n\n

Empecemos con la organización básica. Supongamos que tenemos dos aplicaciones: MyAppiOS y MyAppMacOS, además de un conjunto de bibliotecas internas. La estructura de archivos podría verse así:

\n\n
Monorepo/\n├── Apps/\n│   ├── MyAppiOS/\n│   │   ├── Sources/\n│   │   └── Package.swift (o project.yml para 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 (manifiesto raíz)\n└── Tuist/ (si usamos Tuist)\n    └── Project.swift\n
\n\n

El Package.swift raíz lo conecta todo. Ejemplo de manifiesto:

\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        // Dependencias externas\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

Book Recommendations