Environment: inyección sin cables
Comparte un modelo con todo un árbol de vistas sin pasarlo mano a mano. @Environment inyecta dependencias de forma limpia y desacoplada.
Pasar un modelo de vista en vista mediante parámetros funciona… hasta que tienes cinco niveles de profundidad y cada vista intermedia lo reenvía sin usarlo. El environment resuelve esto: inyectas el modelo una vez y cualquier vista descendiente lo pide directamente.
- El problema del “prop drilling”.
- Inyectar un modelo con
.environment(...). - Leerlo con
@Environment(Modelo.self). - Los valores del environment del sistema.
El problema: pasar a mano
Sin environment, un modelo que necesita una vista profunda tiene que atravesar todas las intermedias:
// tedioso: B y C no usan el modelo, solo lo reenvían
VistaA(modelo: modelo)
→ VistaB(modelo: modelo)
→ VistaC(modelo: modelo)
→ VistaD(modelo: modelo) // la única que lo usa
La solución: inyectar en el environment
Inyectas el modelo en un punto alto del árbol y desaparece de las firmas intermedias:
@main
struct MiApp: App {
@State private var sesion = SesionModel()
var body: some Scene {
WindowGroup {
ContentView()
.environment(sesion) // inyectado para TODO el árbol
}
}
}
Y cualquier vista descendiente, a cualquier profundidad, lo pide:
struct PerfilView: View {
@Environment(SesionModel.self) private var sesion
var body: some View {
Text("Hola, \(sesion.usuario)")
}
}
Ninguna vista intermedia necesita saber que SesionModel existe. Limpio y desacoplado.
Regla práctica: usa parámetros para datos que una vista concreta necesita (el Tarea que muestra una fila), y environment para dependencias transversales que muchas vistas comparten (la sesión, el tema, un router, el modelo de datos global). El environment no es para todo: abusar de él oculta las dependencias y dificulta ver qué necesita cada vista. Para lo compartido de verdad, es perfecto; para lo local, un parámetro explícito es más claro.
Bindings desde el environment
¿Necesitas editar una propiedad del modelo del environment? Reenvuélvelo con @Bindable dentro del body:
struct AjustesView: View {
@Environment(AjustesModel.self) private var ajustes
var body: some View {
@Bindable var ajustes = ajustes // habilita los $bindings
Toggle("Notificaciones", isOn: $ajustes.notificaciones)
}
}
Los valores del sistema
El environment ya trae decenas de valores del sistema que puedes leer:
@Environment(\.colorScheme) private var esquema // .light o .dark
@Environment(\.dismiss) private var dismiss // cerrar una hoja
@Environment(\.locale) private var locale // idioma/región
// ejemplo: un botón que cierra la pantalla actual
Button("Cerrar") { dismiss() }
\.dismiss es especialmente útil: cualquier vista presentada como hoja puede cerrarse a sí misma sin que el padre le pase nada.
Puedes crear tus propios valores de environment (con @Entry en iOS moderno) para inyectar configuración a medida por todo el árbol. Empieza usando los del sistema y los modelos @Observable; los valores personalizados llegan cuando los necesites.
- Crea un modelo
@Observable(p. ej.SesionModelcon unusuario). - Inyéctalo en el
WindowGroupcon.environment(...). - Léelo en una vista profunda con
@Environment(SesionModel.self). - En otra vista, usa
@Bindable varlocal para editar una propiedad con unToggle. - Añade un botón “Cerrar” que use
@Environment(\.dismiss).