In SwiftUI, @Bindable
is used to create binding to mutable properties of observable class instances, ensuring synchronization between ParentView
and ChildView
. If I replace @Bindable
with @Binding
, it simply requires an additional $
sign. Why we need @Bindable
if @Binding
can achieve the same result that is synchronization of all related views when person.name
is modified. What is the key difference in how they work?
▪︎@Bindable
, ChildView(person: person)
@Observable
class People {
var name = "No name"
}
struct ChildView: View {
@Bindable var person: People
var body: some View {
VStack {
TextField("...", text: $person.name)
}
}
}
struct ParentView: View {
@State private var person = People()
var body: some View {
VStack {
TextField("...", text: $person.name)
// no `$` sign
ChildView(person: person)
}
}
}
▪︎@Binding
, ChildView(person: $person)
@Observable
class People {
var name = "No name"
}
struct ChildView: View {
@Binding var person: People
var body: some View {
VStack {
TextField("...", text: $person.name)
}
}
}
struct ParentView: View {
@State private var person = People()
var body: some View {
VStack {
TextField("...", text: $person.name)
// with `$` sign
ChildView(person: $person)
}
}
}
If Apple have to create a separate property wrapper like @Bindable
to achieve two-way binding with @Observable
class, I think @Binding
should not be fully compatible with @Observable
class.
I am expecting the key reason for needing @Bindable
rather than using @Binding
and really appreciate any answers to figure out the key.
@Binding
is compatible with @Observable
.
The main difference is that @Binding
allows the child view to change entirely which instance of People
it is, in addition to getting Binding
s of the properties of People
.
For example:
struct ChildView: View {
@Binding var person: People
var body: some View {
VStack {
TextField("...", text: $person.name)
Button("Change Person") {
// I cannot do this if 'person' is '@Bindable'
person = People()
person.name = "New Person!"
}
}
}
}
See also the documentation for @State
, which also mentions this.
State
properties provide bindings to their value. When storing an object, you can get aBinding
to that object, specifically the reference to the object. This is useful when you need to change the reference stored in state in some other subview, such as setting the reference tonil
[...]
It also says:
However, passing a
Binding
to an object stored inState
isn’t necessary when you need to change properties of that object.
If you need a binding to a specific property of the object, pass either the binding to the object and extract bindings to specific properties where needed, or pass the object reference and use the
Bindable
property wrapper to create bindings to specific properties.
So use @Binding
if the child view needs to change the whole reference to something else (e.g. a totally different instance of People
). If you only want Binding
s to the object's properties, use @Bindable
.