Reputation: 29
I'm kind of confused about the usage of @Bindable. I know this property wrapper is used when we have @State class var at the parent view and Use @Bindable at the child view so we can pass the class variable to child view and update the value, but I notice even without the @Bindable at the child view, the class variable would still reflect change made in the child view at parent view page.
My Parent View
import SwiftUI
struct parentView: View {
@State var model = testModel(test: "string", num: 3, arr: ["a","b"])
@State var isPresented = false
var body: some View {
NavigationStack{
Text(model.test)
NavigationLink("Pass") {
childView(model: model)
}
}
}
}
#Preview {
parentView()
}
My child view
import SwiftUI
struct childView: View {
var model: testModel
@Environment(\.dismiss) var dismiss
var body: some View {
VStack{
Text("Hello, World!")
Button("Change") {
changeModel()
dismiss()
}
}
}
func changeModel(){
model.test = "Undefined"
}
}
#Preview {
childView(model:testModel(test: "string", num: 3, arr: ["a","b"]))
}
My class model
import Foundation
@Observable
class testModel{
var test: String
var num: Int
var arr:[String]
init(test: String, num: Int, arr: [String]) {
self.test = test
self.num = num
self.arr = arr
}
}
I think my parent view can reflect the changes may because the class are reference type. if that is the case, when should we use @Bindable at child view to update the class var
Upvotes: 1
Views: 131
Reputation: 273540
You are correct that you can just pass the @Observable
object across views and everything will be updated correctly.
Compare this to the ObservableObject
API, where you would need a @ObservedObject
property to in the child view for it to update correctly. With @Observable
, you don't need such a property wrapper.
However, the @ObservedObject
property wrapper also allows you to get a Binding
of the object's properties using the $
prefix.
@ObservedObject var foo: SomeObservableObject
var body: some View {
// You get a Binding<String> by writing '$foo.someProperty'!
TextField("Some Text", text: $foo.someProperty)
}
but you cannot do this with an @Observable
object if you don't use a property wrapper:
// no need for @ObservedObject here!
let foo: SomeObservableMacroObject
var body: some View {
// but '$foo' doesn't exist because 'foo' is not wrapped by a property wrapper
TextField("Some Text", text: $foo.someProperty)
}
This is where @Bindable
is needed. Its purpose is to allow you to get a Binding
from an @Observable
object.
@Bindable var foo: SomeObservableMacroObject
var body: some View {
// Just like the first code snippet, you get a Binding<String> by writing '$foo.someProperty'!
TextField("Some Text", text: $foo.someProperty)
}
See also Migrating from the Observable Object protocol to the Observable macro
Upvotes: 0