Cualquier iOS Developer que haya pasado del clásico UIKit al paradigma declarativo sabe que las reglas del juego han cambiado. Al principio, la promesa era simple: menos código, interfaces más rápidas y una reactividad nativa. Sin embargo, a medida que las aplicaciones crecen, nos enfrentamos al mismo monstruo de siempre: el código espagueti. Si estás inmerso en la programación Swift, te habrás dado cuenta de que una mala organización puede transformar la magia declarativa en una pesadilla de mantenimiento.
En este artículo, vamos a desglosar paso a paso cuál es la mejor estructura de proyecto para SwiftUI. Aprenderás cómo organizar tu código en Xcode para crear aplicaciones escalables, mantenibles y, lo más importante, preparadas para compartir lógica y vistas entre iOS, macOS y watchOS utilizando Swift.
El Cambio de Paradigma: Por qué SwiftUI exige una nueva organización
Durante años, la programación Swift en entornos Apple estuvo dominada por el patrón MVC (Model-View-Controller), a menudo bromeado como Massive View Controller. Con la llegada de SwiftUI, el controlador desaparece en favor de un enfoque reactivo basado en el estado.
Al no tener controladores, los desarrolladores a menudo cometen el error de poner toda la lógica de negocio, las llamadas a la red y el enrutamiento directamente dentro de las vistas (View). Esto resulta en “Massive Views”. Para evitar esto, un iOS Developer moderno debe adoptar arquitecturas como MVVM (Model-View-ViewModel), Redux o TCA (The Composable Architecture). Independientemente del patrón exacto que elijas, la mejor estructura de proyecto para SwiftUI siempre se basará en la separación de responsabilidades y la modularidad.
Organización Orientada a Funcionalidades (Feature-Driven)
En el pasado, era común estructurar los proyectos en Xcode agrupando los archivos por su tipo técnico (Type-Driven):
- Models/
- Views/
- ViewModels/
- Services/
Aunque esto funciona para proyectos minúsculos, es un desastre para aplicaciones empresariales. Si necesitas modificar la pantalla de “Perfil”, tienes que saltar entre cuatro carpetas diferentes, buscando archivos dispersos.
La mejor estructura de proyecto para SwiftUI utiliza un enfoque Feature-Driven (Orientado a Funcionalidades). Aquí, agrupas los archivos según la característica o flujo del usuario.
El Árbol de Directorios Ideal
Para un proyecto multiplataforma en Xcode que soporta iOS, macOS y watchOS, la estructura a nivel de carpetas debería verse así:
MyApp/
│
├── App/
│ ├── MyApp.swift (Entry Point)
│ ├── AppEnvironment.swift
│ └── Configuration/
│
├── Core/
│ ├── Networking/
│ ├── Storage/
│ ├── Extensions/
│ └── DesignSystem/
│ ├── Colors.swift
│ ├── Typography.swift
│ └── Components/
│
├── Features/
│ ├── Authentication/
│ │ ├── Models/
│ │ ├── Views/
│ │ ├── ViewModels/
│ │ └── Services/
│ ├── Dashboard/
│ └── Profile/
│
├── Shared/
│ ├── Models/
│ └── Utilities/
│
├── Platforms/
│ ├── iOS/
│ │ ├── Info.plist
│ │ └── iOSSpecificView.swift
│ ├── macOS/
│ │ ├── Info.plist
│ │ └── macOSSpecificView.swift
│ └── watchOS/
│ ├── Info.plist
│ └── watchOSSpecificView.swift
│
└── Tests/
├── UnitTests/
└── UITests/Analicemos cada una de estas capas en profundidad y cómo benefician tu flujo de trabajo de programación Swift.
1. La Capa App: El Punto de Entrada
La carpeta App contiene la configuración principal de tu aplicación. Todo buen iOS Developer sabe que el punto de entrada (@main) debe estar lo más limpio posible. En SwiftUI, esto significa tu estructura que conforma el protocolo App.
Aquí no debe haber lógica de negocio compleja, sino la inyección de dependencias inicial y la configuración del entorno.
import SwiftUI
@main
struct MyApp: App {
// Inyección del estado global o servicios core
@StateObject private var appEnvironment = AppEnvironment()
var body: some Scene {
WindowGroup {
AppCoordinatorView()
.environmentObject(appEnvironment)
}
}
}Al aislar el AppEnvironment en esta carpeta, centralizas la configuración de servicios analíticos, gestores de sesión y configuraciones de temas globales.
2. La Capa Core: El Motor de la Aplicación
La carpeta Core es donde reside la programación Swift agnóstica a la interfaz de usuario. Este es el corazón técnico del proyecto. Todo lo que esté en Core debe poder ser utilizado por cualquier característica de la aplicación sin generar dependencias circulares.
Componentes clave del Core:
- Networking: Tu cliente HTTP. Aquí defines protocolos de red, interceptores, manejo de tokens y parseo genérico con
Codable. - Storage: Gestión de bases de datos locales (Core Data, SwiftData o Realm) y
UserDefaults. - DesignSystem: Crucial en SwiftUI. En lugar de usar
Color.blueoFont.systemdispersos por todo el código, crea tus propios modificadores y componentes reutilizables. Esto es fundamental para la mejor estructura de proyecto para SwiftUI.
Un ejemplo de un componente del Design System:
import SwiftUI
struct PrimaryButton: View {
let title: String
let action: () -> Void
var body: some View {
Button(action: action) {
Text(title)
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.accentColor)
.cornerRadius(10)
}
}
}Mantener estos elementos visuales en Core/DesignSystem te garantiza consistencia visual en iOS, macOS y watchOS.
3. La Capa Features: El Corazón Funcional
Aquí es donde pasarás la mayor parte de tu tiempo en Xcode. Cada carpeta dentro de Features es un micro-módulo. Piensa en Authentication o Dashboard como mini-aplicaciones independientes.
Si adoptamos MVVM, una característica como la autenticación se desglosaría así:
- Models: Estructuras de datos puras de Swift (Ej.
User,AuthToken). - Services: Protocolos y clases que interactúan con el
Core/Networkingpara hacer las peticiones específicas de esta feature. - ViewModels: Clases observables que manejan el estado y la lógica de negocio. (Usando
@Observableen Swift 5.9+). - Views: Archivos puramente de SwiftUI que observan al ViewModel.
¿Por qué esto es vital para un iOS Developer?
Si mañana decides que el módulo de Authentication necesita ser reescrito, o si lo vas a extraer a un framework independiente para compartirlo con otro proyecto, simplemente tomas la carpeta Authentication y la mueves. No tienes dependencias enredadas en una carpeta global de Views o ViewModels.
4. Desarrollo Multiplataforma: Shared y Platforms
Una de las maravillas de la programación Swift moderna y de SwiftUI es “Aprender una vez, aplicar en todas partes” (Learn once, apply anywhere). Sin embargo, una interfaz que funciona perfecto en el iPhone puede verse terrible en la pantalla del Apple Watch o no aprovechar el espacio en un Mac.
La mejor estructura de proyecto para SwiftUI aborda el desarrollo multiplataforma mediante la separación de la UI específica del dispositivo de la lógica compartida.
La Carpeta Shared
Aquí colocarás los Modelos de Dominio y los Casos de Uso (o ViewModels si la lógica es idéntica). La lógica de “Iniciar Sesión” (hacer la llamada al servidor, guardar el token) es exactamente la misma en iOS, macOS y watchOS. El 90% del código de un buen proyecto Swift multiplataforma debería residir en Core, Features (lógica) y Shared.
La Carpeta Platforms
Aprovechando los “Targets” de Xcode, puedes asignar archivos específicos a plataformas específicas. Si necesitas un diseño drásticamente distinto, puedes crear vistas separadas.
// En la carpeta Platforms/iOS/LoginView_iOS.swift
import SwiftUI
struct LoginView: View {
@State private var viewModel = LoginViewModel()
var body: some View {
VStack {
// Diseño de formulario apilado optimizado para pantallas táctiles
}
}
}
// En la carpeta Platforms/macOS/LoginView_macOS.swift
import SwiftUI
struct LoginView: View {
@State private var viewModel = LoginViewModel()
var body: some View {
HStack {
// Diseño a dos columnas con imagen a la izquierda y formulario a la derecha
}
}
}Al tener el mismo nombre de vista (LoginView) en diferentes Targets, la capa de navegación superior no necesita saber en qué plataforma se está ejecutando; compilará la vista correcta según el Target seleccionado en Xcode.
El Siguiente Nivel: Modularización con Swift Package Manager (SPM)
Para proyectos empresariales grandes, estructurar el proyecto mediante carpetas (Groups) en Xcode ya no es suficiente. El estado del arte para un iOS Developer Senior es dividir la aplicación en múltiples Paquetes de Swift locales usando Swift Package Manager.
En la mejor estructura de proyecto para SwiftUI, tu App de Xcode (el .xcodeproj) es solo una cáscara vacía.
En lugar de carpetas, tu proyecto se ve así:
- App Target: Solo tiene el punto de entrada y enlaza los SPM.
- CorePackage: Un SPM con la capa de red y extensiones compartidas.
- DesignSystemPackage: Un SPM puramente visual.
- AuthFeaturePackage: Un SPM con la feature de login.
Beneficios de Modularizar con SPM en la Programación Swift:
- Tiempos de compilación drásticamente reducidos: Xcode solo recompila el paquete que estás modificando, no toda la aplicación.
- Prevención de dependencias ocultas: En los paquetes de Swift, tienes que declarar explícitamente qué importa qué. Es imposible que la capa
Featuresacceda a algo privado deCoresi no está expuesto públicamente (public). - Múltiples plataformas nativamente: SPM está diseñado para manejar iOS, macOS, tvOS y watchOS de manera fluida, permitiéndote definir en el
Package.swiftqué código se compila para qué plataforma. - Previews de SwiftUI ultrarrápidos: Renderizar una vista en un paquete aislado tarda fracciones de segundo comparado con levantar el entorno de un monolito gigante.
Gestión del Estado y Flujo de Datos
Ningún tutorial de SwiftUI está completo sin hablar del estado. La estructura de carpetas no sirve de nada si el flujo de datos es caótico.
Con la introducción de Swift 5.9, la macro @Observable revolucionó la forma en que conectamos nuestros ViewModels. En la mejor estructura de proyecto para SwiftUI, la inyección de dependencias se realiza de arriba hacia abajo usando el entorno (Environment).
Si tienes servicios en tu Core que necesitan ser accedidos por múltiples Features, inyéctalos en la raíz de tu App:
@Observable
final class AppState {
var isUserLoggedIn: Bool = false
var userProfile: User? = nil
}
// En tu App.swift
@main
struct MyApp: App {
@State private var appState = AppState()
var body: some Scene {
WindowGroup {
RootCoordinatorView()
.environment(appState) // Inyección del nuevo estándar de Swift
}
}
}Dentro de cada carpeta de Feature, las vistas consumirán este estado de manera segura. Mantén la reactividad contenida. Evita usar @AppStorage o llamadas directas a Singleton (ej. NetworkManager.shared) profundamente anidados en las vistas. Pásalo por inyección para mantener la testeabilidad.
La Estrategia de Testing
¿Dónde colocamos las pruebas en esta estructura? En Xcode, crearás Targets de testing que reflejan tu estructura principal.
- UnitTests: Si has seguido esta arquitectura Feature-Driven y has extraído tu lógica en ViewModels y Servicios, probar el código de programación Swift es trivial. Puedes probar la lógica de
AuthViewModelsin necesidad de instanciarSwiftUI. - UITests: Se centran en flujos completos. Aquí pruebas la integración visual de las vistas construidas en tus carpetas.
Si utilizas SPM para tus módulos, cada paquete (ej. AuthFeaturePackage) tendrá su propia carpeta Tests interna, lo que mantiene el código de prueba junto a la característica que está probando. Es un principio de cohesión altísimo valorado por cualquier iOS Developer.
Resumen de Mejores Prácticas
Para garantizar que mantienes la mejor estructura de proyecto para SwiftUI, ten en cuenta estos mandamientos:
- Las vistas son “tontas”: Una vista de SwiftUI solo debe preocuparse por renderizar la UI y capturar eventos del usuario. Toda la lógica de ramificación (ifs/else complejos de negocio) pertenece al ViewModel o al Modelo.
- Abraza los Protocolos de Swift: En lugar de inyectar clases concretas de tus
Services, inyecta protocolos. Esto permite crearMocksinstantáneos para tus Previews de Xcode y tus tests unitarios. - Aísla las dependencias de terceros: Nunca uses directamente SDKs de terceros en tus vistas (como Firebase o Alamofire). Crea un envoltorio (Wrapper) en tu capa
Core. Si algún día decides cambiar de proveedor, solo modificas un archivo enCoreen lugar de docenas de vistas enFeatures. - Prioriza el código limpio sobre los “hacks” de UI: Si notas que estás peleando contra el framework para lograr una animación o diseño en específico multiplataforma, da un paso atrás. A menudo, separar los enfoques por plataforma en la carpeta
Platformses más limpio que tener un archivo lleno de#if os(iOS)y#elseif os(macOS).
Conclusión
El ecosistema de Apple evoluciona a un ritmo vertiginoso. Ser un iOS Developer destacado hoy en día no se trata solo de conocer la sintaxis de la programación Swift, sino de entender cómo diseñar sistemas robustos.
La mejor estructura de proyecto para SwiftUI en Xcode es aquella que escala contigo. Al adoptar un enfoque Feature-Driven, separar tu capa lógica (Core) de la visual (Features/Views), y abrazar la modularización con Swift Package Manager, estarás creando aplicaciones para iOS, macOS y watchOS que son un placer mantener, extender y, sobre todo, testear.








