@EnvironmentObject es un property wrapper en SwiftUI que permite a cualquier View en la jerarquía acceder a un ObservableObject sin pasarlo explícitamente a través de una cadena de inicializadores. El objeto se inyecta en el entorno mediante el modificador .environmentObject() en un nivel específico de la jerarquía, después de lo cual todas las View hijas pueden acceder a él a través de @EnvironmentObject. Esto elimina la necesidad de pasar el objeto a través de View intermedias que no lo usan — el llamado prop drilling. Según un artículo de John Sundell — Swift by Sundell (2025), @EnvironmentObject es especialmente útil para datos entre pantallas: sesión de usuario, configuración de la aplicación, gestor del carrito de compras o caché local de datos.
Puntos clave
@EnvironmentObject es un property wrapper que permite a las View de SwiftUI acceder a un ObservableObject desde el entorno de la aplicación. El entorno es un contenedor en el que se pueden colocar objetos en cualquier nivel de la jerarquía de View mediante el modificador .environmentObject(). Una vez que un objeto se coloca en el entorno, cualquier View hija puede acceder a él simplemente declarando una propiedad con @EnvironmentObject y especificando el tipo del objeto.
El propósito principal de @EnvironmentObject es resolver el problema de pasar datos a través de una jerarquía profunda de View sin tener que pasar el objeto por cada nivel intermedio. En aplicaciones complejas con NavigationStack, TabView y ventanas modales ramificadas, @EnvironmentObject simplifica significativamente la arquitectura al eliminar el código repetitivo.
Según Apple Developer Documentation — Environment (2025), @EnvironmentObject utiliza un mecanismo interno de SwiftUI basado en PreferenceKey y la identificación de View. Cada View almacena una referencia a su propio entorno, que se hereda de la View padre y se puede ampliar mediante .environmentObject(). La búsqueda del objeto asciende por la jerarquía hasta la View raíz.
class UserSession: ObservableObject {
@Published var isLoggedIn = false
@Published var userName: String = ""
func login(name: String) {
userName = name
isLoggedIn = true
}
}
@main
struct MyApp: App {
@StateObject var session = UserSession()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(session)
}
}
}
@EnvironmentObject funciona basándose en un mecanismo de inyección de dependencias (DI) integrado en SwiftUI. Cuando llamas a .environmentObject() en una View, SwiftUI almacena el objeto en un almacenamiento especial asociado con esa View y todos sus descendientes. Cuando una View hija declara @EnvironmentObject del mismo tipo, SwiftUI busca el objeto en el entorno, ascendiendo por la jerarquía de padres.
Una característica importante — el tipo del objeto se utiliza como clave para la búsqueda en el entorno. Si hay dos objetos del mismo tipo en el entorno, SwiftUI encuentra el más cercano a la View actual en la jerarquía. Cuando el objeto se inyecta a nivel de WindowGroup, se vuelve globalmente disponible para todas las pantallas de la aplicación, lo que es conveniente para servicios de propósito general.
Según objc.io — SwiftUI Architecture (2025), internamente @EnvironmentObject utiliza un mecanismo similar a @ObservedObject, pero con una capa adicional de abstracción para encontrar el objeto en la jerarquía. SwiftUI no copia el objeto ni crea uno nuevo — pasa una referencia a la instancia existente, por lo que los cambios en el objeto son automáticamente visibles para todas las View que usan @EnvironmentObject.
Tanto @EnvironmentObject como @ObservedObject realizan la misma función básica: suscriben una View a los cambios en un ObservableObject. La diferencia está en el mecanismo de paso del objeto. @ObservedObject requiere un paso explícito a través de un inicializador, mientras que @EnvironmentObject recupera el objeto del entorno sin especificación explícita en cada View intermedia.
| Característica | @EnvironmentObject | @ObservedObject |
|---|---|---|
| Paso | Mediante .environmentObject() en el nivel de jerarquía | A través del inicializador de cada View |
| Visibilidad de dependencias | Oculta — no visible en la firma de la View | Explícita — visible en el init de la View |
| View intermedias | No conocen el objeto | Deben pasar el objeto más adelante |
| Riesgo de error | Error en tiempo de ejecución cuando falta el objeto | Verificación en tiempo de compilación (si el parámetro es obligatorio) |
| Prop drilling | Elimina | Requiere paso manual |
La elección entre @EnvironmentObject y @ObservedObject depende de la arquitectura. Si el objeto se necesita en lo profundo de la jerarquía y en muchas pantallas — @EnvironmentObject es más conveniente. Si la arquitectura requiere una especificación explícita de dependencias para pruebas y legibilidad — @ObservedObject es preferible.
El escenario más común es una sesión de usuario que debe ser accesible en todas las pantallas de la aplicación. Al inyectar UserSession mediante .environmentObject() en la raíz de la aplicación, cualquier pantalla puede acceder a los datos del usuario y al estado de autorización.
struct ProfileView: View {
@EnvironmentObject var session: UserSession
var body: some View {
VStack {
if session.isLoggedIn {
Text("Hello, \(session.userName)")
Button("Logout") {
session.isLoggedIn = false
}
} else {
LoginView()
}
}
}
}
struct SettingsView: View {
@EnvironmentObject var session: UserSession
var body: some View {
Form {
Text("Logged in as \(session.userName)")
}
}
}
Observa que ni ProfileView ni SettingsView reciben la sesión a través de un inicializador. Simplemente declaran @EnvironmentObject var session: UserSession, y SwiftUI encuentra automáticamente el objeto en el entorno. Esto permite añadir nuevas pantallas sin cambiar el código existente de transferencia de datos.
El principal riesgo de @EnvironmentObject es un error en tiempo de ejecución si el objeto no se ha inyectado en el entorno. A diferencia de los parámetros opcionales, @EnvironmentObject no puede ser nil. Si una View con @EnvironmentObject aparece en pantalla y la View padre no ha llamado a .environmentObject() para ese tipo, la aplicación falla inmediatamente con “Fatal error: No ObservableObject of type X found”.
Si inyectas dos objetos del mismo tipo en diferentes niveles de jerarquía, la View hija recibirá el más cercano por jerarquía. Esto puede causar confusión si el desarrollador espera que el objeto del entorno raíz esté disponible en una ventana modal que tiene su propio entorno con un objeto del mismo tipo.
Con la evolución de SwiftUI, han surgido enfoques alternativos de gestión de dependencias que solucionan algunas carencias de @EnvironmentObject — principalmente la implicitud de las dependencias y el riesgo de errores en tiempo de ejecución.
La elección del enfoque depende del tamaño del equipo y la complejidad de la aplicación. Para proyectos pequeños, @EnvironmentObject funciona muy bien. Para proyectos grandes con docenas de pantallas y requisitos estrictos de pruebas, es preferible el paso explícito mediante @ObservedObject o un contenedor DI.
Preguntas frecuentes
Sí, una View puede declarar tantos @EnvironmentObject de diferentes tipos como necesite. SwiftUI busca cada tipo de forma independiente en el entorno. Esto es útil cuando una View necesita acceder a la sesión del usuario, la configuración y el carrito de compras simultáneamente — cada objeto se inyecta por separado.
El Preview falla con un error en tiempo de ejecución al intentar mostrar la View. Añade siempre .environmentObject() en Preview para las View que usan @EnvironmentObject. Utiliza objetos simulados con datos de prueba para que el Preview funcione correctamente y muestre un estado realista.
No, @EnvironmentObject solo funciona con un tipo de clase concreta que conforme a ObservableObject. Para protocolos necesitas usar type erasure o un envoltorio: crea una clase envoltorio que contenga una referencia al objeto de tipo protocolo e inyecta el envoltorio mediante @EnvironmentObject.
Crea una instancia de ObservableObject con datos de prueba y pásala a la View mediante .environmentObject(testObject) en la prueba. Este es el patrón estándar para pruebas de UI en SwiftUI. Para pruebas unitarias, aísla la lógica en el ObservableObject y pruébala por separado de la View.
@EnvironmentObject no crea una carga adicional de rendimiento porque solo pasa una referencia al objeto, no una copia. Sin embargo, las actualizaciones frecuentes de propiedades @Published en un objeto global pueden provocar que muchas View se redibujen simultáneamente, lo que puede afectar al rendimiento.
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