99

Been playing around with SwiftUI and understood the concept of BindableObjects etc so far (at least I hope I do).

I bumped into a stupid problem I can't seem to find an answer for: How do you initialize a @Binding variable?

I have the following code:

struct LoggedInView : View {

    @Binding var dismissView: Bool

    var body: some View {
        VStack {
            Text("Hello World")
        }
    }
}

In my preview code, I want to pass that parameter of type Binding<Bool>:

#if DEBUG
struct LoggedInView_Previews : PreviewProvider {
    static var previews: some View {
        LoggedInView(dismissView: **Binding<Bool>**)
    }
}
#endif

How would I go an initialize it? tried:

Binding<Bool>.init(false)
Binding<Bool>(false)

Or even:

@Binding var dismissView: Bool = false

But none worked... any ideas?

Zev Eisenberg
  • 8,080
  • 5
  • 38
  • 82
DonBaron
  • 1,354
  • 1
  • 10
  • 18

6 Answers6

213

When you use your LoggedInView in your app you do need to provide some binding, such as an @State from a previous view or an @EnvironmentObject.

For the special case of the PreviewProvider where you just need a fixed value you can use .constant(false)

E.g.

#if DEBUG
struct LoggedInView_Previews : PreviewProvider {
    static var previews: some View {
        LoggedInView(dismissView: .constant(false))
    }
}
#endif
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • That looks elegant. I just wonder if it makes the property immutable for the live preview. – Martin R Jun 20 '19 at 13:15
  • It is, but that is typically all you need for the preview – Paulw11 Jun 20 '19 at 21:06
  • 2
    For a static preview, using immutable Binding.constant is fine, but it is totally unusable for a Live Preview where I wanna play around with just one view and see the value changed. – NeverwinterMoon Jan 07 '20 at 09:53
  • The solution of @NeverwinterMoon below is better, because it lets you interact with the canvas. – Noyer282 Jan 24 '20 at 10:37
83

Using Binding.constant(false) is fine but only for static previews. If you actually wanna launch a Live Preview, constant will not behave the same way as the real case as it will never be updated by your actions. I personally use Live Preview a lot, as I can play around with an isolated view.

Here is what I do for previews requiring Binding:

import SwiftUI

struct SomeView: View {
   @Binding var code: String

   var body: some View {
     // some views modifying code binding
   }
}

struct SomeView_Previews: PreviewProvider {
  static var previews: some View {
    PreviewWrapper()
  }

  struct PreviewWrapper: View {
    @State(initialValue: "") var code: String

    var body: some View {
      SomeView(code: $code)
    }
  }
}
NeverwinterMoon
  • 2,363
  • 21
  • 24
  • This works. But I'm wondering why adding a property like `@State static var code: String = ""` isn't enough? – bcause Feb 27 '20 at 01:33
  • 2
    It isn't enough as it doesn't work, unlike the proposed solution. With static var the property value just doesn't get updated and it works the same way as using Binding.constant - which is fine for a static preview only. – NeverwinterMoon Feb 28 '20 at 10:31
  • NeverwinterMoon I've used this in my code and it works like a charm (I needed a @State since I had a Binding<> call). Gave answer +1 – Ethan Halprin Apr 16 '21 at 08:08
12
  • If you need a simple property that belongs to a single view you should use @State
  • If you need to have complex property that may belong to several view(like 2-3 views) you shall use @ObjectBinding
  • Lastly, if you need to have property that needs to use all around views you shall use @EnvironmentObject. Source for detail information

For your case, if you still would like to initialize your Binding variable you can use:

var binding: Binding = .constant(false)
atalayasa
  • 3,310
  • 25
  • 42
  • Note that since Xcode 11.3 Beta 5, (AT)ObjectBinding is now called (AT)ObservedObject (where (AT) = @) – gepree Sep 15 '22 at 10:11
2

In preview you have to use .constant(Bool(false)):

#if DEBUG
struct LoggedInView_Previews : PreviewProvider {
    static var previews: some View {
        LoggedInView(dismissView: .constant(Bool(false))
    }
}
#endif
MasterMind
  • 89
  • 7
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 23 '22 at 03:22
1

if you have an object like viewModel you can also use .constant()

struct View_Previews: PreviewProvider {
  static var previews: some View {
     View(vm:.constant(ViewModel(text: "Sample Text")))
  }
}
caglar
  • 279
  • 2
  • 5
0

I'm using different configurations of my view within one preview (I'm working on a custom control and want to see a different configuration of it). I've extended the implementation provided by @NeverwinterMoon in order to create multiple independent instances of a view.

struct SomeView: View {
   @Binding var value: Int

   var body: some View {
     // some views modifying code binding
   }
}

struct SomeView_Previews: PreviewProvider {
  static var previews: some View {
    VStack {
      // The same view but with different configurations

      // Configuration #1
      PreviewWrapper() { value in
        SomeView(value: value)
          .background(Color.blue)
      }

      // Configuration #2      
      PreviewWrapper(initialValue: 2) { value in
        SomeView(value: value)
          .padding()
      }
    }
  }

  struct PreviewWrapper<Content: View>: View {
    @State var value: Int
    private let content: (Binding<Int>) -> Content
    
    init(
      initialValue: Int = 0,
      @ViewBuilder content: @escaping (Binding<Int>) -> Content
    ) {
      self.value = initialValue
      self.content = content
    }
    
    var body: some View {
      content($value)
    }
  }
}
Andrew Bogaevskyi
  • 2,251
  • 21
  • 25