Skip to content

Commit 66c6ca5

Browse files
stephencelismluisbrown
authored andcommitted
Update bindable helpers docs (#783)
* Update bindable helpers docs * wip
1 parent 193217d commit 66c6ca5

File tree

1 file changed

+42
-30
lines changed

1 file changed

+42
-30
lines changed

Sources/ComposableArchitecture/SwiftUI/Binding.swift

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@
144144
///
145145
/// Binding actions can also be tested in much the same way regular actions are tested. Rather
146146
/// than send a specific action describing how a binding changed, such as
147-
/// `displayNameChanged("Blob")`, you will send a ``Reducer/binding(action:)`` action that
147+
/// `.displayNameChanged("Blob")`, you will send a ``Reducer/binding(action:)`` action that
148148
/// describes which key path is being set to what value, such as `.set(\.$displayName, "Blob")`:
149149
///
150150
/// ```swift
@@ -244,7 +244,7 @@
244244
}
245245
}
246246

247-
/// An action type that exposes a `binding` case for the purpose of reducing.
247+
/// An action type that exposes a `binding` case that holds a ``BindingAction``.
248248
///
249249
/// Used in conjunction with ``BindableState`` to safely eliminate the boilerplate typically
250250
/// associated with mutating multiple fields in state.
@@ -334,8 +334,8 @@
334334
/// Useful in transforming binding actions on view state into binding actions on reducer state
335335
/// when the domain contains ``BindableState`` and ``BindableAction``.
336336
///
337-
/// For example, we can model an app that can bind a number to a stepper and make a network
338-
/// request to fetch a number fact with the following domain:
337+
/// For example, we can model an app that can bind an integer count to a stepper and make a
338+
/// network request to fetch a fact about that integer with the following domain:
339339
///
340340
/// ```swift
341341
/// struct AppState: Equatable {
@@ -373,12 +373,13 @@
373373
/// The view may want to limit the state and actions it has access to by introducing a
374374
/// view-specific domain that contains only the state and actions the view needs. Not only will
375375
/// this minimize the number of times a view's `body` is computed, it will prevent the view
376-
/// from accessing state or sending actions outside its purview.
376+
/// from accessing state or sending actions outside its purview. We can define it with its own
377+
/// bindable state and bindable action:
377378
///
378379
/// ```swift
379380
/// extension AppView {
380381
/// struct ViewState: Equatable {
381-
/// var count: Int
382+
/// @BindableState var count: Int
382383
/// let fact: String?
383384
/// // no access to any other state on `AppState`, like child domains
384385
/// }
@@ -391,10 +392,10 @@
391392
/// }
392393
/// ```
393394
///
394-
/// And in order to transform `BindingAction<ViewState>` into `BindingAction<AppState>`, we
395-
/// need a writable key path from `AppState` to `ViewState`, which we can get by defining a
396-
/// computed property with a getter and setter, where the setter can communicate any updates to
397-
/// bindable view state to the store:
395+
/// In order to transform a `BindingAction<ViewState>` sent from the view domain into a
396+
/// `BindingAction<AppState>`, we need a writable key path from `AppState` to `ViewState`. We
397+
/// can synthesize one by defining a computed property on `AppState` with a getter and a setter.
398+
/// The setter should communicate any mutations to bindable state back to the parent state:
398399
///
399400
/// ```swift
400401
/// extension AppState {
@@ -405,32 +406,43 @@
405406
/// }
406407
/// ```
407408
///
408-
/// Finally, in the view we can use ``Store/scope(state:action:)-9iai9`` to pluck out view
409-
/// state, embed view actions, and transform binding actions between domains:
409+
/// With this property defined it is now possible to transform a `BindingAction<ViewState>` into
410+
/// a `BindingAction<AppState>`, which means we can transform a `ViewAction` into an
411+
/// `AppAction`. This is where `pullback` comes into play: we can unwrap the view action's
412+
/// binding action on view state and transform it with `pullback` to work with app state. We can
413+
/// define a helper that performs this transformation, as well as route any other view actions
414+
/// to their reducer equivalents:
410415
///
411416
/// ```swift
412-
/// var body: some View {
413-
/// WithViewStore(
414-
/// self.store.scope(
415-
/// state: { .init(count: $0.count, fact: $0.fact) }
416-
/// action: {
417-
/// switch $0 {
418-
/// case let .binding(action):
419-
/// return .binding(action.pullback(\.view)) // transform binding action
420-
/// case .factButtonTapped:
421-
/// return .factButtonTapped
422-
/// }
423-
/// }
424-
/// )
425-
/// ) { viewStore in
426-
/// ...
417+
/// extension AppAction {
418+
/// static func view(_ viewAction: AppView.ViewAction) -> Self {
419+
/// switch viewAction {
420+
/// case let .binding(action):
421+
/// // transform view binding actions into app binding actions
422+
/// return .binding(action.pullback(\.view))
423+
///
424+
/// case let .factButtonTapped
425+
/// // route `ViewAction.factButtonTapped` to `AppAction.factButtonTapped`
426+
/// return .factButtonTapped
427+
/// }
427428
/// }
428429
/// }
429430
/// ```
430431
///
431-
/// This is a lot, though both state and action transformations could be pulled out to their own
432-
/// helpers. Importantly, the view has whittled away its domain and can only read state it has
433-
/// access to, and send actions it has access to.
432+
/// Finally, in the view we can invoke ``Store/scope(state:action:)-9iai9`` with these domain
433+
/// transformations to leverage the view store's binding helpers:
434+
///
435+
/// ```swift
436+
/// WithViewStore(
437+
/// self.store.scope(state: \.view, action: AppAction.view)
438+
/// ) { viewStore in
439+
/// Stepper("\(viewStore.count)", viewStore.$count)
440+
/// Button("Get number fact") { viewStore.send(.factButtonTapped) }
441+
/// if let fact = viewStore.fact {
442+
/// Text(fact)
443+
/// }
444+
/// }
445+
/// ```
434446
///
435447
/// - Parameter keyPath: A key path from a new type of root state to the original root state.
436448
/// - Returns: A binding action over a new type of root state.

0 commit comments

Comments
 (0)