Saltar al contenido

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:

SWIFT
// tedioso: B y C no usan el modelo, solo lo reenvíanVistaA(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:

SWIFT
@mainstruct 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:

SWIFT
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.

Cuándo environment y cuándo parámetros

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:

SWIFT
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:

SWIFT
@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 actualButton("Cerrar") { dismiss() }

\.dismiss es especialmente útil: cualquier vista presentada como hoja puede cerrarse a sí misma sin que el padre le pase nada.

Environment values propios

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.

Inyecta sin cables

  1. Crea un modelo @Observable (p. ej. SesionModel con un usuario).
  2. Inyéctalo en el WindowGroup con .environment(...).
  3. Léelo en una vista profunda con @Environment(SesionModel.self).
  4. En otra vista, usa @Bindable var local para editar una propiedad con un Toggle.
  5. Añade un botón “Cerrar” que use @Environment(\.dismiss).