@Binding è un Property Wrapper in SwiftUI che crea un riferimento a dati posseduti da un altro componente. Binding non memorizza il valore da solo — fornisce solo l'accesso alla fonte di verità esistente attraverso la proiezione $. Secondo la Documentazione Sviluppatori Apple (2025), Binding fornisce una comunicazione reattiva bidirezionale tra la vista padre e quella figlia senza possesso diretto dei dati. @Binding è il meccanismo chiave per passare lo stato mutabile verso il basso nella gerarchia.
Punti chiave
@Binding è un Property Wrapper che crea una connessione bidirezionale tra una proprietà memorizzata nella vista padre e un componente figlio. La differenza principale tra Binding e @State: Binding non possiede i dati. Legge e scrive valori solo attraverso la fonte vera — @State, @StateObject o un altro Binding nel padre. Senza Binding, le viste figlie non potrebbero modificare lo stato dell'antenato senza callback o delegati.
Binding è implementato come una struttura con due proprietà: wrappedValue (il valore corrente) e projectedValue (il Binding stesso, accessibile tramite $). Quando una vista figlia modifica wrappedValue attraverso Binding, SwiftUI trasmette la modifica alla fonte dati e ridisegna tutte le viste dipendenti. Questo avviene in modo sincrono all'interno del ciclo di aggiornamento corrente.
Una caratteristica importante: @Binding non si limita al passaggio a un singolo livello. Binding può essere passato attraverso più livelli di gerarchia — ogni componente figlio riceve un riferimento alla stessa fonte dati. Una modifica a qualsiasi livello attiva un unico aggiornamento di tutte le viste legate.
Il meccanismo di binding bidirezionale attraverso @Binding si basa sulle proiezioni di Property Wrapper. Quando un padre dichiara @State var value: T, SwiftUI genera automaticamente la proiezione $value di tipo Binding<T>. Passando $value a un componente figlio con @Binding var value: T, si collegano entrambe le viste alla stessa cella di memoria. Qualsiasi scrittura attraverso Binding nella vista figlia attiva un ridisegno di entrambi i componenti.
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)
}
}
Nell'esempio, SliderContainer possiede @State value, e SliderView riceve il Binding attraverso $value. Lo Slider all'interno di SliderView è legato a questo Binding. Trascinando il cursore, Slider modifica il valore attraverso Binding, che aggiorna automaticamente @State in SliderContainer, ed entrambe le viste visualizzano il numero corrente. L'intera catena funziona senza un singolo callback o notifica.
Per creare un Binding da @StateObject o @ObservedObject, si usa la stessa proiezione: $object.property fornisce Binding<PropertyType>. Ciò consente di passare proprietà individuali di ObservableObject alle viste figlie senza passare l'intero oggetto. Questo approccio fornisce un accoppiamento più stretto e previene ridisegni non necessari.
Prima di SwiftUI, il modo standard per passare le modifiche verso l'alto nella gerarchia era attraverso callback e delegati: il padre passava una closure, il componente figlio la invocava al momento della modifica. @Binding offre un'alternativa con meno codice e una sintassi più dichiarativa. Invece di passare una closure di completamento, si passa semplicemente $stateValue.
| Criterio | @Binding | Callback |
|---|---|---|
| Codice | Un'annotazione + $ | Closure + invocazione |
| Multilivello | Automatico | Catena di closure |
| Test | Binding(value:constant) | Closure mock |
| Leggibilità | Alta | Media |
| Flessibilità | Solo dati | Qualsiasi logica |
Usa @Binding quando la vista figlia ha solo bisogno di leggere e modificare un valore. Se sono necessari effetti collaterali al momento della modifica (validazione, registrazione, richiesta di rete), combina Binding con un callback: passa Binding per i dati e una closure per gli eventi. Ad esempio, un TextField può essere legato a un Binding, mentre onChange attiva la validazione.
@Binding viene utilizzato in diversi scenari tipici. Il primo — controlli personalizzati: interruttori, cursori, selettori di colore e altri elementi interattivi accettano Binding per la sincronizzazione bidirezionale. Il secondo — finestre modali: il flag di visualizzazione del sheet viene passato come Binding, consentendo alla vista figlia di chiudersi tramite presentationMode o impostazione diretta.
Il terzo pattern — moduli con separazione. Se un modulo è composto da molti campi, ogni campo può essere estratto in un componente separato che accetta un Binding per il suo valore. Ciò semplifica il test e il riutilizzo dei campi tra diversi moduli. Il componente padre rimane l'unico proprietario dell'intero modello del modulo.
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)
}
}
}
Il componente FormField accetta un titolo e un Binding a una stringa. Mostra un'etichetta e un TextField legato al Binding passato. Qualsiasi modulo può usare FormField più volte passando $property per ogni campo. Ciò riduce la duplicazione del markup e centralizza la stilizzazione dei campi di testo.
SwiftUI consente di creare Binding manualmente tramite l'inizializzatore Binding(get:set:). Ciò è utile quando è necessario aggiungere logica alla lettura o scrittura di un valore. Ad esempio, si può creare un Binding che formatta un numero prima di salvarlo, o un Binding che sincronizza il valore con un server remoto a ogni modifica.
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)
}
}
Nel listato, il emailBinding personalizzato converte automaticamente il testo in minuscolo e rimuove gli spazi a ogni modifica. Il TextField usa questo Binding invece di legarsi direttamente a $email. Questo approccio centralizza la validazione e la trasformazione dei dati all'interno del Binding senza ingombrare il codice con gestori onChange.
Il primo e più comune errore è passare un valore invece di un Binding. Se un componente figlio dichiara @Binding var text: String, e il padre passa text (senza $), il compilatore darà un errore: Cannot convert value of type 'String' to expected argument type 'Binding<String>'. La soluzione — usa sempre il prefisso $ quando passi: $text.
Il secondo errore — Binding su dati di sola lettura. Se la vista figlia ha solo bisogno di leggere un valore, non usare @Binding — basta un semplice let o @State dal padre. Binding implica la capacità di scrittura, e permessi di modifica eccessivi complicano il debug e violano il principio del minimo privilegio.
Il terzo problema — Binding.constant in produzione. Binding.constant(value) crea un legame fittizio senza feedback — le modifiche vengono ignorate. Usa constant solo per prototipazione e anteprime (Xcode Previews), ma mai nel codice reale. Per i test, usa Binding(get:set:) con comportamento controllato.
Domande frequenti
@State possiede i dati e gestisce la loro memorizzazione nell'heap. @Binding riferisce solo lo stato esistente senza possesso. @State è sempre privato, @Binding è un parametro di input della vista figlia.
Sì, tramite l'inizializzatore Binding(get:set:) o Binding.constant(value). Binding può anche essere ottenuto da @StateObject attraverso la proiezione $object.$property e da Publisher tramite Binding(get:set:) all'interno di Subscribe.
@Binding viene passato a catena: ogni componente intermedio dichiara @Binding e lo passa oltre tramite $. Tutti i livelli fanno riferimento alla stessa fonte dati nella vista radice.
Binding.constant crea un involucro muto — il setter ignora i nuovi valori. È destinato solo alla prototipazione e alle Previews SwiftUI dove non è richiesto feedback dal componente figlio.
Sì, Binding<T?> è supportato. Se passi Binding<String?>, la vista figlia potrà impostare nil. Ciò è comodo per campi modulo opzionali o stati con possibilità di reset.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche