wandres.dev
SWIFTUI AVANZADO · @Observable y animaciones

@Observable y el estado compartido

El framework Observation reemplaza a ObservableObject: clases reactivas con la macro @Observable, sin @Published ni boilerplate. El corazón del estado moderno en SwiftUI.

⏱ 15 min

@State es genial para datos locales, pero las apps reales necesitan modelos compartidos entre muchas vistas: un carrito, la sesión del usuario, los ajustes. Para eso llegó el framework Observation con la macro @Observable: reactividad sin ceremonia.

🎯 Al terminar esta lección sabrás
  • La macro @Observable y qué genera.
  • Usar un modelo observable en las vistas.
  • @Bindable para bindings a sus propiedades.
  • Por qué reemplazó a ObservableObject.

El modelo observable

Marca una clase con @Observable y SwiftUI observará automáticamente cualquier propiedad que uses en una vista:

import Observation

@Observable
class CarritoModel {
    var items: [String] = []
    var total = 0.0

    func añadir(_ item: String, precio: Double) {
        items.append(item)
        total += precio
    }
}

Sin @Published, sin objectWillChange, sin nada. SwiftUI detecta qué propiedades lee cada vista y solo la redibuja cuando esas cambian.

Usarlo en una vista

La vista que “posee” el modelo lo guarda en @State (sí, @State también sirve para objetos observables):

struct TiendaView: View {
    @State private var carrito = CarritoModel()

    var body: some View {
        VStack {
            Text("Total: \(carrito.total, format: .currency(code: "EUR"))")
            Button("Añadir café") {
                carrito.añadir("Café", precio: 1.5)
            }
        }
    }
}

Cambias carrito.total desde el método y la vista se actualiza sola.

Observación granular = rendimiento

La magia de @Observable: SwiftUI rastrea qué propiedades lee de verdad cada vista. Si una vista solo muestra carrito.total, cambiar carrito.items no la redibuja. Con el viejo ObservableObject + @Published, cualquier cambio en el objeto redibujaba todo lo suscrito. Ahora la actualización es quirúrgica: solo se recalcula lo que depende del dato que cambió. Modelos grandes sin penalización de rendimiento.

@Bindable: bindings a un modelo

¿Necesitas un TextField o Toggle conectado a una propiedad del modelo? @Bindable te da los $bindings:

struct AjustesView: View {
    @Bindable var ajustes: AjustesModel

    var body: some View {
        Form {
            TextField("Nombre", text: $ajustes.nombre)     // $ del modelo
            Toggle("Modo oscuro", isOn: $ajustes.oscuro)
        }
    }
}

@Bindable es el puente entre un objeto @Observable y los controles que piden un Binding.

Adiós a ObservableObject

Si ves código con class X: ObservableObject, @Published var, @StateObject y @ObservedObject, es el sistema anterior. La tabla de equivalencias:

Antiguo Moderno (Observation)
class: ObservableObject @Observable class
@Published var var (normal)
@StateObject var @State var
@ObservedObject var var o @Bindable var
@EnvironmentObject @Environment(Modelo.self)
💡
Empieza siempre con @Observable

Para cualquier proyecto nuevo con iOS 17+, usa @Observable desde el principio: menos código, mejor rendimiento y es el camino que Apple recomienda. El sistema antiguo solo lo verás en código heredado. La comunicación entre vistas mediante @Environment la ves en la próxima lección.

⚔️ Comparte estado de verdad
  1. Crea una clase @Observable con una propiedad y un método que la cambie.
  2. Posee el modelo en una vista con @State private var.
  3. Muestra una propiedad y un botón que llame al método; observa la actualización.
  4. Crea otra vista con @Bindable y conecta un TextField a $modelo.propiedad.