Diseño de sistemas
Guía de Arquitectura de iOS
Un recorrido por cómo se diseñan las apps de iOS a escala — patrones, límites de módulos, flujo de datos, concurrencia y lanzamiento. Este es el material de "cómo construiría una app iOS grande" que diferencia las respuestas de senior y arquitecto del trabajo de funcionalidades.
01 · Patrones de arquitectura: MVC → MVVM → TCA
iOS ha pasado del MVC de UIKit (que a menudo se convertía en "Massive View Controller") a MVVM, donde un view model mantiene la lógica de presentación y el estado, y la vista permanece delgada y testeable. El flujo de datos de SwiftUI es naturalmente compatible con MVVM; algunos equipos van más lejos con un enfoque unidireccional / TCA (The Composable Architecture): estado en un lugar, cambios vía acciones a través de un reducer, efectos secundarios aislados y testeables.
02 · Modularización con paquetes Swift
A medida que una app crece, un solo target se convierte en un cuello de botella: compilaciones lentas, dependencias enredadas y dolor de merge. Divídalo en paquetes Swift locales — módulos de funcionalidad más paquetes compartidos Core / DesignSystem / Networking — con una dirección de dependencia explícita (las funcionalidades dependen del core, nunca entre sí).
03 · Inyección de dependencias y testeabilidad
Dependa de protocolos, no de tipos concretos, y pase las dependencias (inyección por
initializer o @Environment de SwiftUI) en lugar de recurrir a singletons. Esa única disciplina
hace que los view models sean testeables con fakes, desacopla las funcionalidades de la infraestructura y
le permite intercambiar implementaciones (por ejemplo, un stub API en previews).
URLSession.shared o un singleton global internamente no puede probarse sin la red.
"Inyecto un protocolo APIClient para que el view model nunca hable con la red directamente — eso es
lo que lo hace testeable con un fake."04 · Red y patrón repository
Ponga un repository entre su UI y las capas de red/persistencia. Los view models piden al
repository los modelos de dominio; el repository posee el APIClient, descodificación, caché y la
decisión de cuándo servir datos en caché vs frescos. Esto mantiene una única fuente de verdad y un único
lugar para agregar reintentos, renovación de auth y paginación.
05 · Offline-first y sincronización
Para apps que deben funcionar sin conexión, haga del almacenamiento local la fuente de verdad: la UI siempre lee de SwiftData/Core Data, y un motor de sincronización reconcilia con el servidor en segundo plano. Necesita una estrategia para la resolución de conflictos (último escritor gana, servidor autoritativo o fusión por campo), rastreo de cambios y reintento de mutaciones fallidas.
06 · Arquitectura de navegación
Centralice el enrutamiento para que la navegación sean datos, no NavigationLinks
esparcidos. Un router/coordinator posee una ruta NavigationStack de valores
Route; las funcionalidades emiten rutas, el router decide cómo presentarlas. Los deep links y
notificaciones push se descodifican en los mismos valores Route, y la restauración de estado
se convierte en "persistir y recargar la ruta".
07 · Arquitectura de concurrencia
Diseñe el aislamiento deliberadamente: la UI y los view models se ejecutan en el
@MainActor; el estado mutable compartido (cachés, tiendas en memoria) vive detrás de
actors; el trabajo costoso se ejecuta en tareas de fondo y vuelve al actor principal solo para
publicar resultados. Bajo Swift 6, hacer los tipos Sendable y respetar el aislamiento se
aplica en tiempo de compilación — por lo que la arquitectura debe ser intencional, no
accidental.
DispatchQueue.main.async
y locks por todas partes es síntoma de un aislamiento no diseñado. "Pongo el estado mutable
compartido detrás de actors y fijo la UI a @MainActor para que el compilador demuestre que estoy libre de
carreras, en vez de vigilarlo por convención."08 · Observabilidad, banderas y lanzamiento
La preparación para producción es parte de la arquitectura. Conecte logging estructurado (OSLog), métricas/MetricKit y reporte de crashes; proteja funcionalidades riesgosas detrás de feature flags para que pueda distribuir a oscuras e implementar gradualmente; y use phased release en App Store con un plan de rollback (una bandera para desactivar, o una corrección expedita). Defina presupuestos — lanzamiento en frío, tasa sin crashes — y monitoreelos.
Inmersiones
El playbook de senior en la forma de concepto → ejemplo → problema → solución, para que cada idea se fije como una decisión de ingeniería real en lugar de una definición.
El monolito modular con SPM
Sincronización offline-first
Estado compartido basado en actors
await sus métodos. Swift 6 entonces demuestra la ausencia de carreras en tiempo
de compilación. "Recurro a un actor en lugar de un lock porque hace la carrera imposible, no solo
improbable."Un feed de imágenes que mantiene 120fps
LazyVStack y dé a las filas
identidad estable para que SwiftUI las reutilice. "Los frames perdidos casi siempre son trabajo
de decodificación filtrándose al hilo principal — muévalo, cachee el resultado, y el frame rate se
resuelve solo."