@Binding es un Property Wrapper en SwiftUI que crea una referencia a los datos que posee otro componente. Binding no almacena el valor por sí mismo — solo proporciona acceso a la fuente de verdad existente a través de la proyección $. Según la Documentación para Desarrolladores de Apple (2025), Binding proporciona una comunicación reactiva bidireccional entre una vista padre y una hija sin posesión directa de los datos. @Binding es el mecanismo clave para pasar estado mutable hacia abajo en la jerarquía.
Puntos clave
@Binding es un Property Wrapper que crea una conexión bidireccional entre una propiedad almacenada en la vista padre y un componente hijo. La principal diferencia entre Binding y @State: Binding no posee los datos. Solo lee y escribe valores a través de la fuente verdadera — @State, @StateObject u otro Binding en el padre. Sin Binding, las vistas hijas no podrían modificar el estado del ancestro sin callbacks o delegados.
Binding se implementa como una estructura con dos propiedades: wrappedValue (el valor actual) y projectedValue (el propio Binding, accesible mediante $). Cuando una vista hija cambia wrappedValue a través de Binding, SwiftUI transmite el cambio a la fuente de datos y redibuja todas las vistas dependientes. Esto ocurre sincrónicamente dentro del ciclo de actualización actual.
Una característica importante: @Binding no se limita a la transmisión de un solo nivel. Binding se puede pasar a través de varios niveles de jerarquía — cada componente hijo recibe una referencia a la misma fuente de datos. Un cambio en cualquier nivel desencadena una única actualización de todas las vistas enlazadas.
El mecanismo de enlace bidireccional a través de @Binding se basa en las proyecciones de Property Wrappers. Cuando un padre declara @State var value: T, SwiftUI genera automáticamente la proyección $value de tipo Binding<T>. Al pasar $value a un componente hijo con @Binding var value: T, conectas ambas vistas a la misma celda de memoria. Cualquier escritura a través de Binding en la vista hija provoca un redibujado de ambos componentes.
struct SliderContainer: View {
@State private var value: Double = 0.5
var body: some View {
VStack {
Text("Value: \(value)")
SliderView(value: $value)
}
}
}
struct SliderView: View {
@Binding var value: Double
var body: some View {
Slider(value: $value, in: 0...1)
}
}
En el ejemplo, SliderContainer posee @State value, y SliderView recibe el Binding a través de $value. El Slider dentro de SliderView está vinculado a este Binding. Al arrastrar el control deslizante, Slider cambia el valor a través de Binding, lo que actualiza automáticamente @State en SliderContainer, y ambas vistas muestran el número actual. Toda la cadena funciona sin un solo callback o notificación.
Para crear un Binding desde @StateObject o @ObservedObject se usa la misma proyección: $object.property da Binding<PropertyType>. Esto permite pasar propiedades individuales de ObservableObject a vistas hijas sin pasar el objeto completo. Este enfoque proporciona un acoplamiento más preciso y evita redibujados innecesarios.
Antes de SwiftUI, la forma estándar de pasar cambios hacia arriba en la jerarquía era mediante callbacks y delegados: el padre pasaba un cierre y el componente hijo lo invocaba al cambiar. @Binding ofrece una alternativa con menos código y una sintaxis más declarativa. En lugar de pasar un cierre de finalización, simplemente pasas $stateValue.
| Criterio | @Binding | Callbacks |
|---|---|---|
| Código | Una anotación + $ | Cierre + invocación |
| Multinivel | Automático | Cadena de cierres |
| Pruebas | Binding(value:constant) | Cierres mock |
| Legibilidad | Alta | Media |
| Flexibilidad | Solo datos | Cualquier lógica |
Usa @Binding cuando la vista hija solo necesite leer y modificar un valor. Si se requieren efectos secundarios al cambiar (validación, registro, solicitud de red), combina Binding con un callback: pasa Binding para los datos y un cierre para los eventos. Por ejemplo, un TextField puede enlazarse a un Binding, mientras que onChange activa la validación.
@Binding se utiliza en varios escenarios típicos. El primero — controles personalizados: interruptores, deslizadores, selectores de color y otros elementos interactivos aceptan Binding para sincronización bidireccional. El segundo — ventanas modales: la bandera de visualización de sheet se pasa como Binding, permitiendo que la vista hija se cierre mediante presentationMode o un ajuste directo.
El tercer patrón — formularios con separación. Si un formulario consta de muchos campos, cada campo se puede extraer en un componente separado que acepte un Binding para su valor. Esto simplifica las pruebas y la reutilización de campos en diferentes formularios. El componente padre sigue siendo el único propietario de todo el modelo del formulario.
struct FormField: View {
let title: String
@Binding var text: String
var body: some View {
VStack(alignment: .leading) {
Text(title).font(.caption)
TextField("Enter \(title.lowercased())", text: $text)
.textFieldStyle(.roundedBorder)
}
}
}
El componente FormField acepta un título y un Binding a una cadena. Muestra una etiqueta y un TextField vinculado al Binding pasado. Cualquier formulario puede usar FormField múltiples veces pasando $property para cada campo. Esto reduce la duplicación de marcado y centraliza el estilo de los campos de texto.
SwiftUI permite crear Binding manualmente a través del inicializador Binding(get:set:). Esto es útil cuando se necesita agregar lógica al leer o escribir un valor. Por ejemplo, se puede crear un Binding que formatee un número antes de guardarlo, o un Binding que sincronice el valor con un servidor remoto en cada cambio.
struct ValidatedField: View {
@State private var email: String = ""
var emailBinding: Binding<String> {
.init(
get: { email },
set: { email = $0.lowercased().trimmingCharacters(in: .whitespaces) }
)
}
var body: some View {
TextField("Email", text: emailBinding)
}
}
En el listado, el emailBinding personalizado convierte automáticamente el texto a minúsculas y elimina espacios en cada cambio. El TextField usa este Binding en lugar de enlazar directamente a $email. Este enfoque centraliza la validación y transformación de datos dentro del Binding sin saturar el código con manejadores onChange.
El primer error y el más común es pasar un valor en lugar de un Binding. Si un componente hijo declara @Binding var text: String, y el padre pasa text (sin $), el compilador dará un error: Cannot convert value of type 'String' to expected argument type 'Binding<String>'. La solución — usar siempre el prefijo $ al pasar: $text.
El segundo error — Binding en datos de solo lectura. Si la vista hija solo necesita leer un valor, no uses @Binding — basta con un let simple o @State del padre. Binding implica la capacidad de escritura, y los permisos de modificación excesivos complican la depuración y violan el principio de privilegio mínimo.
El tercer problema — Binding.constant en producción. Binding.constant(value) crea un enlace ficticio sin retroalimentación — los cambios se ignoran. Usa constant solo para prototipado y previsualizaciones (Xcode Previews), pero nunca en código real. Para pruebas, usa Binding(get:set:) con comportamiento controlado.
Preguntas frecuentes
@State posee los datos y gestiona su almacenamiento en el montón. @Binding solo hace referencia al estado existente sin posesión. @State siempre es privado, @Binding es un parámetro de entrada de la vista hija.
Sí, a través del inicializador Binding(get:set:) o Binding.constant(value). También se puede obtener un Binding desde @StateObject mediante la proyección $object.$property y desde Publisher mediante Binding(get:set:) dentro de Subscribe.
@Binding se pasa por cadena: cada componente intermedio declara @Binding y lo pasa hacia adelante mediante $. Todos los niveles hacen referencia a la misma fuente de datos en la vista raíz.
Binding.constant crea una envoltura muda — el setter ignora los nuevos valores. Está destinado solo para prototipado y Previews de SwiftUI donde no se requiere retroalimentación del componente hijo.
Sí, Binding<T?> es compatible. Si pasas Binding<String?>, la vista hija podrá establecer nil. Esto es conveniente para campos de formulario opcionales o estados con posibilidad de reinicio.
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