SwiftUI es un framework declarativo de Apple para construir interfaces de usuario en todas las plataformas del ecosistema. En lugar de describir pasos de forma imperativa, el desarrollador declara cómo debe verse la interfaz y SwiftUI gestiona su renderizado y actualización. Según la Apple Developer Documentation (2025), SwiftUI es compatible con iOS 15+, iPadOS 15+, macOS 12+, watchOS 8+ y tvOS 15+, y utiliza el View Protocol como bloque constructivo básico para todos los componentes de la interfaz.
Puntos clave
body es la base de cualquier componente UI de SwiftUI, devolviendo una descripción de pantalla mediante composición de vistas.SwiftUI es un framework declarativo presentado por Apple en 2019 para reemplazar UIKit en nuevos proyectos. En lugar de crear manualmente instancias de UIView y añadirlas a la jerarquía, el desarrollador describe la interfaz a través de estructuras que cumplen con el protocolo View. SwiftUI calcula automáticamente la diferencia entre el estado actual y el nuevo, y redibuja solo las partes cambiadas usando su propio motor de renderizado.
El framework está escrito en Swift utilizando value semantics (estructuras, no clases), lo que hace que los componentes UI sean ligeros y thread-safe. A diferencia de UIKit, donde UIViewController puede pesar 200+ bytes debido al runtime de Objective-C, una SwiftUI View es simplemente una estructura de unos pocos bytes. Esto es especialmente importante para watchOS con su memoria limitada.
La misma descripción de View funciona en iPhone, iPad, Mac, Apple Watch, Apple TV y Apple Vision Pro. SwiftUI adapta la interfaz a la plataforma: gestos táctiles en iOS, combinaciones de teclado en macOS, desplazamiento con Digital Crown en watchOS. Esto reduce el tiempo de desarrollo para empresas que lanzan aplicaciones en múltiples plataformas Apple, pero requiere configuración adicional para elementos específicos de cada plataforma.
En SwiftUI, cada pantalla es una estructura que implementa el protocolo View con un único requisito: una propiedad computada body de tipo some View. La palabra clave some (opaque type) oculta el tipo concreto de la vista, permitiendo a SwiftUI optimizar el renderizado. Dentro de body, el desarrollador combina componentes listos — Text, Image, Button, List — mediante ViewBuilder, que ensambla múltiples vistas en una.
struct GreetingView: View {
let name: String
var var body: some View {
VStack {
Text("¡Hola, \(name)!")
.font(.title)
.foregroundColor(.blue)
Image(systemName: "hand.wave")
.imageScale(.large)
}
.padding()
}
}
En el ejemplo, VStack (stack vertical) contiene Text e Image. El valor de name se pasa a través del inicializador de la estructura — así funciona DI (Dependency Injection) en SwiftUI sin contenedores DI externos. Cada modificador devuelve una nueva vista con el cambio aplicado, sin mutar el original. Esto es posible gracias a la inmutabilidad de los value types.
ViewBuilder es un result builder anotado con @resultBuilder que ensambla hasta 10 vistas en una. Dentro de body se pueden usar if/else, switch y ForEach sin envoltorios adicionales. ForEach trabaja con elementos Identifiable — a cada vista se le asigna un id único para una animación correcta durante inserción/eliminación.
En SwiftUI, el estado determina qué contenido se muestra en pantalla. Cuando el estado cambia, SwiftUI recrea el body de la vista dependiente y compara el resultado con el anterior usando un algoritmo diff. Para almacenar el estado se utilizan property wrappers — cada uno resuelve su tarea específica: estado local, conexión con una vista hija o modelo de datos externo.
struct CounterView: View {
@State private var count = 0
var var body: some View {
VStack {
Text("Contador: \(count)")
Button("Incrementar") {
count += 1
}
}
}
}
class UserViewModel: ObservableObject {
@Published var name = ""
@Published var age = 0
}
@State almacena un valor local simple (Int, String, Bool) dentro de la estructura View. SwiftUI mueve la memoria de la estructura a un almacenamiento separado — por lo tanto, una propiedad con @State puede mutarse incluso si View es un value type. @ObservableObject es para clases con propiedades @Published, cuyos cambios notifican automáticamente a SwiftUI la necesidad de redibujar.
@Binding crea una conexión bidireccional con una fuente de datos ubicada en la vista padre. El padre pasa $variable (projected value), y el hijo lee y escribe el valor a través del binding. Esto permite mover la entrada de texto o un interruptor a un componente separado manteniendo el estado en el padre. Sin @Binding, cada cambio requeriría un closure callback para pasar el nuevo valor hacia arriba.
Antes de iOS 16, la navegación en SwiftUI se construía sobre NavigationView — una API heredada con comportamiento complejo en iPad (split view, doble columna). A partir de iOS 16, Apple recomienda NavigationStack — una alternativa simplificada con rutas type-safe. El desarrollador define un enum de rutas posibles, y NavigationStack gestiona automáticamente la pila de pantallas con soporte para enlaces profundos y retorno a la raíz.
enum Route: Hashable {
case detail(id: Int)
case settings
}
struct ContentView: View {
var var body: some View {
NavigationStack {
List {
NavigationLink("Pantalla de detalle",
value: Route.detail(id: 42))
NavigationLink("Ajustes",
value: Route.settings)
}
.navigationDestination(for: Route.self) { route in
switch route {
case .detail(let id): DetailView(id: id)
case .settings: SettingsView()
}
}
}
}
}
Las rutas conformes a Hashable permiten usar cualquier tipo de datos para pasar parámetros. navigationDestination(for:destination:) asocia el tipo de ruta con la vista destino. La ventaja sobre la navegación de UIKit es que no se requiere redibujado al añadir una nueva ruta: basta con añadir un case al enum y un handler en el switch. Los enlaces profundos se manejan a través de processDeepLink en NavigationStack.
Para la navegación programática (tras inicio de sesión, temporizador o respuesta del servidor), se usa @State con el inicializador de NavigationLink: NavigationLink(isActive: $isActive). Cuando isActive = true, la transición se realiza sin toque del usuario. Una alternativa es el binding del array $path en NavigationStack: $path.append(Route.detail(id: 1)).
Modifier es un método que devuelve una copia modificada de la vista. A diferencia de UIKit, donde la configuración de propiedades se realiza mediante mutación de una vista existente, SwiftUI crea un nuevo valor con el cambio aplicado. El encadenamiento de modificadores construye la interfaz final a partir de transformaciones secuenciales: fuente → padding → color → sombra → gesto.
Apple proporciona más de 200 modificadores integrados. Los más comunes: .font(), .foregroundColor(), .padding(), .background(), .cornerRadius(), .shadow(), .opacity(), .offset(). El orden de los modificadores importa: .padding() antes de .background() rellena el área con padding, después — solo el área interior. Los modificadores personalizados se crean mediante el protocolo ViewModifier.
Los modificadores se pueden aplicar condicionalmente mediante el operador ternario: .foregroundColor(isError ? .red : .primary). Para animación se usa .animation(.easeInOut, value: state) — el modificador de animación se vincula a una propiedad de estado específica. Cuando esta propiedad cambia, SwiftUI anima la transición entre el valor antiguo y el nuevo. La animación funciona con opacity, offset, scale, rotation, tamaño y color — cada propiedad tiene su correspondiente AnimatableParameter.
Para animaciones personalizadas están disponibles .transition (aparición/desaparición) y .matchedGeometryEffect (transición suave de un elemento entre dos contenedores). Este último se usa para hero animation en listas: un icono en una celda de lista se transforma suavemente en una imagen grande en la pantalla de detalle.
La elección entre SwiftUI y UIKit es uno de los primeros dilemas del desarrollador iOS. Ambos frameworks son compatibles con Apple, pero resuelven el problema de construcción de interfaces de formas fundamentalmente diferentes: SwiftUI declarativamente, UIKit imperativamente. La diferencia se manifiesta en la gestión del estado, la navegación, el rendimiento y la compatibilidad.
| Aspecto | SwiftUI | UIKit |
|---|---|---|
| Enfoque | Declarativo: qué mostrar | Imperativo: cómo construir |
| Estado | Property Wrappers, redibujado automático | Manual: reloadData, setNeedsLayout |
| Código UI | Compacto, cadenas de modificadores | Verboso, NSCoder/Storyboard/restricciones |
| Rendimiento | Alto en iOS 17+, algoritmo diff | Máximo en iOS 12–16, control directo |
| Versión mínima | iOS 15+ (soporte completo) | iOS 2+ (todas las versiones) |
Para nuevos proyectos con versión mínima iOS 17, Apple recomienda SwiftUI como framework principal. UIKit sigue siendo necesario para interfaces que requieren control fino sobre el renderizado (UICollectionViewLayout personalizado, escenas CAAnimation complejas) o soporte para iOS 12–14. Muchos proyectos usan un enfoque híbrido: SwiftUI mediante UIHostingController se integra en una app UIKit, y UIViewRepresentable permite usar componentes UIKit dentro de la jerarquía SwiftUI.
Preguntas frecuentes
Sí, mediante UIHostingController (SwiftUI en UIKit) y UIViewRepresentable (UIKit en SwiftUI). Es un enfoque híbrido, popular durante la migración.
iOS 17 ofrece funcionalidad completa: NavigationStack, Observation framework, Swift Charts. iOS 15 es el umbral mínimo para producción.
La causa más frecuente es cambiar una propiedad @Published en un hilo secundario. ObservableObject debe enviar los cambios en el main actor: @MainActor class ViewModel.
Usa .debounce mediante Combine: Button.publisher(for: .tap) .debounce(for: .seconds(0.3), scheduler: RunLoop.main).
Sí, mediante los modificadores Gesture: DragGesture, LongPressGesture, MagnificationGesture, RotationGesture. Combínalos usando .simultaneousGesture() y .sequenced().
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también