Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing ViewModel field in SwiftUI using Xcode 12: "Accessing State's value outside of being installed on a View"

I think this error message is new to SwiftUI in Xcode 12 since it gave 0 hits in Google while the message itself is fairly generic:

Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.

I have the following code (removed some fluff):

public struct ContentView: View {
    @ObservedObject var model: RootViewModel

    public var body: some View {
        VStack(alignment: .center, content: {
            Picker(selection: model.$amount, label: Text("Amount")) {
                Text("€1").tag(1)
                Text("€2").tag(2)
                Text("€5").tag(5)
                Text("€10").tag(10)
            }.pickerStyle(SegmentedPickerStyle())
            Text("Donating: €\(model.amount)").font(.largeTitle)
        }).padding(.all, 20.0)
    }
}

public class RootViewModel: ObservableObject {
    @State public var amount: Int = 1
}

I used to have the field right in the ContentView and that worked alright. Now the UI does not update anymore and I got that run-time warning instead.

like image 853
Lucas van Dongen Avatar asked Jun 29 '20 08:06

Lucas van Dongen


2 Answers

Thanks to @Andrew's answer I figured out how to make it work again. First you change the @State to @Published:

    @Published public var amount: Int = 1

Next, you need to change how your Picker is bound to the data:

            Picker(selection: $model.amount, label: Text("Amount")) {
                Text("€1").tag(1)
                Text("€2").tag(2)
                Text("€5").tag(5)
                Text("€10").tag(10)
            }.pickerStyle(SegmentedPickerStyle())

So we went from model.$amount to $model.amount.

like image 157
Lucas van Dongen Avatar answered Oct 20 '22 19:10

Lucas van Dongen


Similar to the other answer, an App is different from a View. You'll get the same error message but the cause is less obvious than in the OPs example.

@main
struct MyCoolApp: App {
    @SceneStorage("testSceneStorage") private var persisted = ""

    var body: some Scene {
        WindowGroup {
            Text(persisted) // "Accessing a SceneStorage value outside of being installed on a View. This will always return the default value."
        }
    }
}

To me the word installed in this error message is confusing. I wish they'd gone with something like, "Accessing a SceneStorage / State value that is not defined on a view".

like image 2
Joe Susnick Avatar answered Oct 20 '22 19:10

Joe Susnick