0

SOLVED, thanks to @JoakimDanielson. Hope this helps others...

I was trying to understand @State and @Binding, and was getting stuck in the preview for SwiftUI.

As you will see, I was trying to learn how to store the model outside of the view, and give controls (e.g., the "up!!" button) the power to update the model.

The app:

import SwiftUI

@main
struct BinnderApp: App {
    @State private var appModel: Model = Model(value: 42)
    
    var body: some Scene {
        WindowGroup {
            ContentView(myModel: $appModel)
        }
    }
}

struct Model {
    var value: Int
    
    mutating func increment() {
        value += 1
    }
}

The view:

import SwiftUI

struct ContentView: View {
    @Binding  var myModel: Model
    
    var body: some View {
        VStack {
            Button("up!!") {
                myModel.increment()
            }
            Text("the value is \(myModel.value)")
        }
    }
}

#Preview {
    //  @Binding var myModel: Model = Model(value: 0). //. this was the problem
    @Previewable @State var myModel: Model = Model(value: 0) //  solution!
    ContentView(myModel: $myModel)
}

In the [original] first line of #Preview, I got this error: Cannot convert value of type 'Model' to specified type 'Binding<Model>'

Of course I tried different combinations of things to squash the error, no joy. Joakim gave me the solution: use @Previewable @State, which I never would have seen.

Let's take a moment to see why @State makes sense.

Use @State to specify the original source location of some data that you plan to use in a different scope. Use @Binding in that destination (usually a View) to declare that data. Using @Binding gives the View read-write access to the data.

So in the App above, I declare the appModel property as a @State variable; then I pass it to the ContentView using the dollar sign to indicate that it is a binding. In the View, it's a property (myModel) declared as a @Binding.

The problem for me came in setting up the Preview. The key to the solution (besides @Previewable, still a bit of a mystery to me) is that even though the Preview is coded in the "View" file, it still calls ContentView() just like in the "App" file. So the Model that gets passed in has to be declared @Source (and initialized) in that "Preview" section.

3
  • 1
    Replace@Binding with @Previewable @State. You want a State and not Binding property wrapper to start with an with the new Preview macro you can use Previable to declare such property Commented Oct 13, 2024 at 17:30
  • @JoakimDanielson Great! Thanks! And...trying to understand the "why" check me on this: (1) @State tells us that this is the ultimate source of the data, as it is in the "app" file. Here, in this #Preview thing, that code—which calls ContentView()—substitutes for the app for the purposes of creating that view. Right? Commented Oct 13, 2024 at 18:53
  • And (2) @JoakimDanielson, where in the world would I have found that out in the tutorials or documentation? Commented Oct 13, 2024 at 18:54

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.