How do I preview a View that's using @Bindable?

I have a SwiftUI view that is being passed a binding from a parent view. I would like to preview the subview, but can't. I tried .constant and a @Model object directly. Both ways crash the preview.

Any idea on how I can get this to work properly?

@Model
class FamilyMember: Identifiable {
	@Attribute(.unique) var id: String { name }
	var name = ""

	init(name: String = "") {
		self.name = name
	}
}


struct MemberView: View {
	@Bindable var member: FamilyMember

	var body: some View {
		Text(member.name)
	}
}

#Preview {
	MemberView(member: FamilyMember(name: "Family member"))
}

#Preview {
	MemberView(member: .constant(FamilyMember(name: "Family member")))
}
Post not yet marked as solved Up vote post of MegaWatt Down vote post of MegaWatt
1.5k views

Replies

This thread on the forum should help: https://developer.apple.com/forums/thread/118589

I copy the answer for easier reading whilst giving credit to the author, Jim Dovey:

creating a wrapper view specifically to enable proper binding support for previews. The reason using an @State within the preview doesn't work is because the 'previews' property isn't considered a 'body' method by the runtime, and @State complains if you try to get bindings outside of a body method call (it calls fatalError()).

struct StatefulPreviewWrapper<Value, Content: View>: View {
    @State var value: Value
    var content: (Binding<Value>) -> Content

    var body: some View {
        content($value)
    }

    init(_ value: Value, content: @escaping (Binding<Value>) -> Content) {
        self._value = State(wrappedValue: value)
        self.content = content
    }
}

// This takes a ViewBuilder block to which it passes a Binding ready to use. You then put it into action like so:

struct ContentView: View {
    @Binding var enabled: Bool

    var body: some View {
        Text("Currently enabled? \(enabled ? "Yes" : "No")")
        Toggle("Toggle Me", isOn: $enabled)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        StatefulPreviewWrapper(false) { ContentView(enabled: $0) }
    }
}
  • I like the strategy, but it's not working for the @Model. When I add the .modelContent below the previewWrapper or the contentView, it still cannot be found.

    failed to find a currently active container for FamilyMember

Add a Comment

@MegaWatt Can you post updated code that includes what you're trying now? That error should be resolved by configuring a model container using the .modelContainer modifier on the view inside your preview.

I found a solution that works better for me here: https://stackoverflow.com/questions/77025438/how-do-i-preview-a-view-thats-using-bindable