'Mutable Binding in SwiftUI Live Preview

I have a ChildView with a variable:

@Binding var itemName: String

In this ChildView I have few buttons that change value of the variable:

Button(action: {
    self.itemName = "different value"
})

I was trying to use Preview like this:

struct ChildView_Previews: PreviewProvider {
    static var previews: some View {
        ChildView(itemName: "test")
    }
}

But I am getting an error:

Cannot convert value of type 'String' to expected argument type 'Binding'

I am aware that I can use Preview like below. And the error will be gone and preview will work, but... itemName will have constant value, it will not be mutable now, not interactive in Live Preview:

struct ChildView_Previews: PreviewProvider {
    static var previews: some View {
        ChildView(itemName: .constant("test"))
    }
}

How to declare a binding in SwiftUI Preview to make it interactive?



Solution 1:[1]

Updates to a @State variable in a PreviewProvider appear to not update the the read-only computed property previews directly. The solution is to wrap the @State variable in a test holder view. Then use this test view inside the previews property so the Live Preview refreshes correctly. Tested and working in Xcode 11.2.1.

struct ChildView: View {
    @Binding var itemName: String

    var body: some View {
        VStack {
            Text("Name: \(itemName)")
            Button(action: {
                self.itemName = "different value"
            }) {
                Text("Change")
            }
        }
    }
}

struct ChildView_Previews: PreviewProvider {

    struct BindingTestHolder: View {
        @State var testItem: String = "Initial"
        var body: some View {
            ChildView(itemName: $testItem)
        }
    }

    static var previews: some View {
        BindingTestHolder()
    }
}

backup

Solution 2:[2]

If you need a value that can be changed in the live preview, I like to use this helper class:

struct BindingProvider<StateT, Content: View>: View {

    @State private var state: StateT
    private var content: (_ binding: Binding<StateT>) -> Content

    init(_ initialState: StateT, @ViewBuilder content: @escaping (_ binding: Binding<StateT>) -> Content) {
        self.content = content
        self._state = State(initialValue: initialState)
    }

    var body: some View {
        self.content($state)
    }
}

Use it like so:

struct YourView_Previews: PreviewProvider {

    static var previews: some View {
        BindingProvider(false) { binding in
            YourView(yourBindingVar: binding)
        }
    }

}

This allows you to test changing the binding in the live preview.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Fabian Streitel