SwiftUI body not reevalutated after binding changed

Viewed 340

A bad case of SwiftUI magic not working for me, and I am loosing my sanity here. Why is the text not updating its value here? Why is the body not reevaluated after each increment() call?

class ReadingStateVM: ObservableObject {
    @Published var value = 0

    func increment() {
        value = value + 1
        print("value \(value)")
    }
}

struct ReadingStateView: View {
    var viewModel = ReadingStateVM()

    var body: some View {
        Text("State \(viewModel.value)")
            .onTapGesture {
                self.viewModel.increment()
        }
    }
}
2 Answers

You need to add the @ObservedObject property wrapper so when changes happen to your view model, the view will also update.

@ObservedObject var viewModel = ReadingStateVM()

In SwiftUI we use structs and mutating funcs for data, not objects. This is because SwiftUI takes advantage of value-semantics for tracking changes. To learn this watch WWDC 2020 Data Essentials in SwiftUI at 3:58. With this in mind, your code should now look like this:

struct ContentViewConfig {
    var counter = 0
    var anotherRelatedVar = 0

    mutating func increment() {
        counter = counter + 1
        print("counter: \(counter)")
    }
}

struct ContentView: View {
    @State var config = ContentViewConfig()

    var body: some View {
        Button("State \(config.counter)"){
            config.increment()
        }
    }
}

Furthermore, ObservableObject is part of the Combine framework so you usually only use it when you want to use assign to set the result of a Combine pipeline to an @Published var. And as the other answer states, this object needs to be declared with @ObservedObject for the body to be called when an @Published property changes.

Related