How to preview a custom View that takes bindings as inputs in its initializer?

Say you have the following view:


import SwiftUI

struct BindingViewExample_1 : View {

    @State var value = false

    private var text: String {
        "Toggle is " + (value ? "'on'" : "'off'")
    }

    var body: some View {
        HStack {
            Toggle(isOn: $value) {
                Text(text)
            }
        }
        .padding([.leading, .trailing], 80)
    }
}

#if DEBUG
struct BindingViewExample_1_Previews : PreviewProvider {

    static var previews: some View {
        BindingViewExample_1(value: true)
    }

}
#endif


This previews fine and you can interact with the control in the preview and see the changes immediately.


But what if it took a *binding* as input? Consider this alternate version of the same view:


import SwiftUI

struct BindingViewExample_2 : View {

    @Binding var value: Bool

    private var text: String {
        "Toggle is " + (value ? "'on'" : "'off'")
    }

    var body: some View {
        HStack {
            Toggle(isOn: $value) {
                Text(text)
            }
        }
        .padding([.leading, .trailing], 80)
    }
}

#if DEBUG
struct BindingViewExample_2_Previews : PreviewProvider {

    @State static var value = false

    static var previews: some View {
        BindingViewExample_2(value: $value)
    }

}
#endif


The only differences are in line 5 for the View itself, and in the preview code for both views. This compiles without errors but the preview no longer works. XCode just refuses to even create a preview.


If you keep the View as is but change the preview code to this, instead,


#if DEBUG
struct BindingViewExample_2_Previews : PreviewProvider {

    static var previews: some View {
        BindingViewExample_2(value: .constant(true))
    }

}
#endif


Xcode now builds and displays a preview but interacting with it doesn't update the view. That's not surprising, given the `.constant(true)` binding, but that's the only way I managed to make the preview display when the View takes a binding and that's not very useful (since I can't interact with it).


So... what's the right way to write preview code for a View that takes bindings as inputs?

I'm not sure how to make the preview interactive but I just added a grouping so I could see the two states as seen below. Hope that helps.

Group { }
#if DEBUG 
struct BindingViewExample_2_Previews : PreviewProvider { 
    static var previews: some View { 
         Group {
             BindingViewExample_2(value: .constant(true)) 
             BindingViewExample_2(value: .constant(false)) 
          }
    } 
} 
#endif

You can add another SwiftUI View that acts as a preview container to hold the state you need for the toggle.


struct BindingViewExamplePreviewContainer_2 : View {
     @State
     private var value = false

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

#if DEBUG
struct BindingViewExample_2_Previews : PreviewProvider {
    static var previews: some View {
        BindingViewExamplePreviewContainer_2()
    }
}
#endif
13

I wound up 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()).


Here's what I use:


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

For what it's worth, this is one of the tools in my toolset on Github.

I used this today, more than 4 years after the original question and answer by [@Rob the Artist](https://developer.apple.com/forums/profile/Rob the Artist). The way I did it with Swift 5 (a bit cleaner) is: #Preview { struct BindingViewExamplePreviewContainer_2 : View { @State private var value = false var body: some View { BindingViewExample_2(value: $value) } }

return BindingViewExamplePreviewContainer_2() }

#Preview { 
    struct BindingViewExamplePreviewContainer : View { 
       @State private var value = false      

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

    return BindingViewExamplePreviewContainer()
}

I used Rob the Artist's solution in a different context and it worked perfectly. I was struggling with how to inject a @Namespace in a class that required initialisation into the preview. I'm a newbie so much appreciated the simplicity of the solution.


#Preview {
    struct RectButtonPreview: View {
        @Namespace var slideLevel
        
        var body: some View {
            RectButtonView(buttonCol: .blue, selected: true)
                  .environment(SlideAnimations(slideLevel: slideLevel))
        }
    }
    return RectButtonPreview()
}

This was driving me mad as well, because half the examples (including in this state answer 'how do I preview a @Binding' with 'here's a working example using @State' which misses the point by a mile. I've found different workarounds elsewhere that also didn't work for me. What does work, however, is to replace @Binding with @Bindable. And suddenly, the standard #Preview macro works out of the box.

#Preview {
BindingViewExample_2(value: true)
}
How to preview a custom View that takes bindings as inputs in its initializer?
 
 
Q