NavigationStack es un contenedor de navegación moderno en SwiftUI, presentado en iOS 16+ y que reemplaza a NavigationView. Según la Documentación para desarrolladores de Apple, 2024, NavigationStack gestiona una pila de pantallas a través de una ruta de navegación con seguridad de tipos (NavigationPath), admite navegación profunda, retorno programático a la pantalla raíz y conservación del estado al cambiar los datos. A diferencia de NavigationView, NavigationStack no requiere envoltura en un contenedor adicional y proporciona un Binding directo a la ruta de navegación.
Puntos clave
NavigationStack es una Vista contenedora que implementa navegación basada en pila (LIFO). Gestiona el historial de transiciones, permitiendo colocar nuevas pantallas en la pila y volver atrás mediante el botón “Atrás” del sistema o de forma programática. NavigationStack forma parte de SwiftUI desde iOS 16, iPadOS 16, macOS 13, watchOS 9 y tvOS 16.
La principal innovación de NavigationStack es la ruta de navegación con seguridad de tipos. En lugar de especificar directamente un destino al crear un NavigationLink, se coloca un valor en la ruta, y la Vista de destino se registra por separado mediante el modificador .navigationDestination(for:destination:). Esto separa la navegación de la representación, haciendo el código más modular y comprobable.
Según WWDC 2022 (Sesión 10054), NavigationStack utiliza un nuevo mecanismo de navegación basado en ObservableObject y el ciclo de vida de SwiftUI. A diferencia de NavigationView, que dependía de UINavigationController internamente, NavigationStack está completamente implementado en SwiftUI, mejorando la previsibilidad y la compatibilidad con el ciclo de vida de SwiftUI.
NavigationStack acepta una Vista raíz y una ruta de navegación opcional (Binding a NavigationPath o un array de valores Hashable). Todas las pantallas hijas se colocan en la pila mediante NavigationLink o añadiendo valores programáticamente a la ruta.
NavigationView fue el contenedor de navegación principal en SwiftUI antes de iOS 16. Gestionaba automáticamente UINavigationController internamente, lo que provocaba varios problemas: comportamiento impredecible al cambiar datos, complejidad con la navegación programática y falta de seguridad de tipos.
| Característica | NavigationStack (iOS 16+) | NavigationView (iOS 13–15) |
|---|---|---|
| Tipo de navegación | Pila (LIFO) | Pila (LIFO) |
| Ruta de navegación | Tipada (NavigationPath) | No soportada |
| Deep linking | Soporte integrado | Requiere soluciones |
| Retorno programático | Mediante ruta (pop, popToRoot) | dismiss, presentationMode |
| Internamente | SwiftUI nativo | UINavigationController |
| Compatibilidad | iOS 16+ | iOS 13+ |
Ventaja clave de NavigationStack — navegación con seguridad de tipos. Se define la ruta como un array de tipos específicos (o NavigationPath para pilas heterogéneas) y se registra un destino para cada tipo. Esto elimina errores de discrepancia de tipos y hace la navegación predecible.
NavigationView está obsoleto en iOS 17. Apple recomienda migrar a NavigationStack para todos los proyectos nuevos y al actualizar la versión mínima a iOS 16.
NavigationPath es un tipo que representa la ruta de navegación en NavigationStack. Puede almacenar valores heterogéneos (AnyHashable) o usarse con un tipo específico mediante Binding a un array [T: Hashable]. NavigationPath se codifica y decodifica automáticamente para conservar el estado.
struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: String.self) { value in
DetailView(id: value)
}
.navigationDestination(for: Int.self) { value in
NumberView(number: value)
}
}
}
func goToRoot() {
path.removeLast(path.count)
}
func pushDeepLink() {
path.append("detail_42")
}
}
Pila heterogénea: NavigationPath puede contener valores de diferentes tipos si implementan Hashable. Por ejemplo, la primera pantalla puede aceptar un String (ID), la segunda un Int (número), la tercera un enum Route personalizado. Cada tipo registra un .navigationDestination separado para su visualización.
Soporte Codable: NavigationPath implementa Codable si todos los valores en la ruta también son Codable + Hashable. Esto permite guardar y restaurar el estado de navegación al reiniciar la aplicación o al entrar en segundo plano.
.navigationDestination(for:destination:) — un modificador que registra una Vista de destino para un tipo de datos específico. Cuando NavigationLink coloca un valor de este tipo en la ruta, SwiftUI encuentra automáticamente el .navigationDestination correspondiente y crea la pantalla.
enum AppRoute: Hashable {
case profile(UserID)
case settings
case about
}
struct AppNavigation: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .profile(let id):
ProfileView(userId: id)
case .settings:
SettingsView()
case .about:
AboutView()
}
}
}
}
}
// Navigate: path.append(AppRoute.profile("user_123"))
Regla importante: .navigationDestination debe aplicarse a una Vista que esté dentro de NavigationStack, y antes de que NavigationLink coloque un valor en la ruta. Generalmente se añade a la Vista raíz o a un contenedor de sección. Si no se encuentra .navigationDestination para el tipo del valor, la transición no se producirá.
Según SwiftUI Engineering (2023), .navigationDestination puede registrarse en diferentes niveles de la jerarquía. SwiftUI busca el destino más cercano durante la navegación. Esto permite sobrescribir el destino para un tipo en diferentes partes de la aplicación.
Patrón 1: navegación mediante enum Route. Defina un enum con valores asociados para todas las pantallas de la aplicación. Use un .navigationDestination para AppRoute y un switch para el enrutamiento. Esto proporciona una única fuente de verdad para todas las transiciones posibles en la aplicación.
struct StoreView: View {
@State private var path: [ProductRoute] = []
var body: some View {
NavigationStack(path: $path) {
ProductGrid()
.navigationDestination(for: ProductRoute.self) { route in
switch route {
case .detail(let product):
ProductDetail(product: product)
case .reviews(let productId):
ReviewsView(productId: productId)
}
}
}
}
}
enum ProductRoute: Hashable {
case detail(Product)
case reviews(String)
}
Patrón 2: navegación programática y deep linking. NavigationStack permite la gestión programática de la pila: añadir, eliminar pantallas y volver a la raíz. Esto es necesario para notificaciones push, deep links y restauración de la navegación tras un reinicio.
Patrón 3: array de tipos en lugar de NavigationPath. Si todas las pantallas usan el mismo tipo (por ejemplo, String o un enum personalizado), use Binding a [T]. Esto proporciona una tipificación más estricta y mejor rendimiento que NavigationPath. NavigationPath está justificado para pilas heterogéneas con diferentes tipos de pantallas.
Según Point-Free (2024), NavigationStack con enum Route es la forma preferida de organizar la navegación en aplicaciones SwiftUI. Hace que todas las transiciones posibles sean explícitas, seguras en cuanto a tipos y comprobables, lo que es especialmente importante para proyectos grandes con decenas de pantallas.
Preguntas frecuentes
NavigationStack — un contenedor de navegación SwiftUI (iOS 16+) que gestiona una pila de pantallas mediante una ruta con seguridad de tipos. Reemplazó a NavigationView, ofreciendo soporte para deep linking, navegación programática y conservación del estado.
NavigationStack utiliza una ruta con seguridad de tipos (NavigationPath) en lugar de enlazar directamente NavigationLink a un destino. Admite navegación programática, deep linking y Codable para la conservación del estado. Funciona en SwiftUI, no a través de UINavigationController.
NavigationPath es un tipo que representa una secuencia de pantallas en la pila. Se añaden valores a la ruta mediante path.append() o NavigationLink con value. Cada tipo registra un .navigationDestination para su visualización. NavigationPath admite Codable y la conservación automática del estado.
Mediante la gestión programática de la ruta: tras procesar la URL, llame a path.append() con el valor de ruta correspondiente. NavigationStack muestra automáticamente la pantalla de destino. Volver a la raíz — path.removeLast(path.count).
Sí, si su versión mínima es iOS 16+. NavigationView está obsoleto en iOS 17. La migración proporciona navegación con seguridad de tipos, soporte de deep linking y una mejor alineación con el ciclo de vida de SwiftUI. Para proyectos con iOS 15 y versiones inferiores, siga usando NavigationView por ahora.
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