Search code examples
swiftuipreviewswiftdata

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")))
}

Solution

  • I think this is a bug but with this wrapper you can work around it.

    struct SwiftDataPreviewWrapper<Content: View>: View {        
        @ViewBuilder var view: Content
        
        let modelContainer: ModelContainer
        
        init<S>(of type: S.Type, view: () -> Content) where S : PersistentModel {
            do {
                modelContainer = try ModelContainer(for: type, configurations: .init(isStoredInMemoryOnly: true))
                
            } catch {
                fatalError("Could not initialize ModelContainer")
            }
            self.view = view()
        }
        var body: some View {
            view
                .modelContainer(modelContainer)
            
        }
    }
    

    Then you can use it with any @Model like this

    #Preview {
        SwiftDataPreviewWrapper(of: FamilyMember.self){
            MemberView(member: FamilyMember(name: "Family member"))
        }
        
    }
    

    The premise behind the wrapper working is that the model container is created and set before the initialization of the object.

    Using the modelContainer ViewModifier causes the same crash that says a container isn’t available.