How to use binding(send localStateToViewAction:)

When viewing the documentation comment it states:

Derives a binding from the store that prevents direct writes to state and instead sends
actions to the store.

The method is useful for dealing with SwiftUI components that work with two-way Bindings
since the Store does not allow directly writing its state; it only allows reading state and
sending actions.

For example, a text field binding can be created like this:

 struct State { var name = "" }
 enum Action { case nameChanged(String) }

 TextField(
   "Enter name",
   text: viewStore.binding(
     send: { Action.nameChanged($0) }
   )
 )
  • Parameters:
    • localStateToViewAction: A function that transforms the binding's value
      into an action that can be sent to the store.
  • Returns: A binding.

However doing something like the following produces an error.

struct TestView: View {
  struct TestState { var name = "" }
  enum TestAction { case nameChanged(String) }
  let store: Store<TestState, TestAction>
  var body: some View {
    WithViewStore(store) { viewStore in
      TextField(
        "Enter name",
        text: viewStore.binding(
          send: { TestAction.nameChanged($0) }
        )
      )
    }
  }
}

I'm just wondering if I'm misunderstanding the usage of binding(send: )? What is the expected usage of this method?

TextField(
        "Enter name",
        text: viewStore.binding(
          send: { TestAction.nameChanged($0) }
        )
      )

I'm not quite sure how the above would work? binding(send: ) is returning Binding<State> when text is expecting a Binding<String> ? binding(get: send:) returns Binding<LocalState> which would map to Binding<String>

Is it possible the documentation is incorrect or out of date? I'm still curious if this (a binding which only sends an action?) is possible if so? Making a binding to a textField thats only one way wouldn't make a whole lot of sense in this situation obviously but a use case like the following may make a one way binding useful:

struct ImageSelectorView: View {
  @Binding var image: Image

  ... Selects an image from the camera role 
}

struct ImagesView: View {
  struct State { var images: [Image] = [] }
  enum Action { case add(Image) }
  let store: Store<State, Action>
  var body: some View {
    WithViewStore(store) { viewStore in
      List {
        ... viewStore.images
      }
      Button { 
        show(ImageSelectorView(image: viewStore.binding(send: .addImage($0)) 
      } label: { 
        Text("ADD") 
      } 
    }
  }
}

In all honesty using a closure instead of an Image Binding would obviously work, or even binding(get: Image(""), send: { .add($0) }) so this question is more just to satisfy my own curiosity. And my initial interpretation of the documentation comment was that I should indeed be able to do just binding(send:).

This is just a typo in the docs. The binding overload that does not take a state transformation is useful for when your view store already holds the value you want to bind.

I've updated the docs here:

Thanks for bring this to our attention!

1 Like
Terms of Service

Privacy Policy

Cookie Policy