Create a mutable @Binding in SwiftUI Previews

Tue, 11 Aug 2020 00:00:00 +0000

Let's see a small tip helping us having a Binding which can change over time when interacting with a SwiftUI preview.

If you played a little bit with SwiftUI chances are you may have wondered about one thing in preview: how to make a real @Binding that can be modified?

struct TalkDetail_Previews: PreviewProvider {
    struct var previews: some View {
        TalkDetail(.constant(Preview.sampleTalk)) // I'm able to make a Binding but actions won't take effect
    }
}

.constant is not what we are looking for here as we want to make modifications to the data. Binding(get:set:) might not be a good fit either: it won't trigger any state update and so no view update. The trick here is to make a in-between view which will store our data and send it back to the view we are testing:

/// Store a (writable) binding transferred to underlying content
struct WithBinding<T, Content: View>: View {
    @State private var data: T
    private let content: (Binding<T>) -> Content

    init(for data: T, @ViewBuilder content: @escaping (Binding<T>) -> Content) {
      self.data = data
      self.content = content
    }

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

You can now change your preview and have your binding working as expected 🚀

struct TalkDetail_Previews: PreviewProvider {
    static var previews: some View {
        WithBinding(for: Preview.sampleTalk) {
            TalkDetail($0)
        }
    }
}