Ting Qu
Ting Qu

Reputation: 29

Swift UI @Bindable

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

Answers (1)

Sweeper
Sweeper

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

Related Questions