RFC: Better Bindings Revisited #2216
Replies: 7 comments 18 replies
-
Is the idea of this to be the "legacy" support until apps could required iOS 17 and get macro support for @observable like you demonstrated? https://hachyderm.io/@pointfreeco/110555023243470033 That said, the updated API looks great and solves an issue I faced with binding actions in a recent app. To make it work, I just removed the bindings entirely and made my own |
Beta Was this translation helpful? Give feedback.
-
How do we create a |
Beta Was this translation helpful? Give feedback.
-
RE this...
I've added an extension onto Store to scope just by an action which I think I prefer...
Personal preference I guess. But something worth mentioning. Means I can do like...
|
Beta Was this translation helpful? Give feedback.
-
Losing the ability to "chain the @BindingState property wrapper to produce child BindingState" is disappointing. However, it's a good tradeoff for ViewState and ViewAction. This seems like a good way forward. |
Beta Was this translation helpful? Give feedback.
-
This is really great - very happy with the simple I've got a couple of questions about how to use it in other use cases. I have a question about using the new shorter syntax when using child struct Feature: Reducer {
struct State: Equatable {
@BindingState var someData: SomeDataType
}
struct SomeOtherData: Equatable {
var someInt: Int?
var someString: String?
}
} If I want to specifically listen out for struct SomeOtherData: Equatable {
@BindingState var someInt: Int?
@BindingState var someString: String?
} How do I reference these properties in a view, using the shortened syntax? Is that possible with these tools? I've tested leaving the parent I'm also wondering how to use the Binding ViewActions where the ViewAction type is defined separately from the context of the Reducer? For example, the couple of places I use ViewActions, they're defined local to the view (as is the ViewState). extension FeatureView {
struct ViewState: Equatable {}
enum ViewAction {
case initialise
}
} but the tooling for ViewActions with Bindings seems to be for where ViewAction is defined as a case of the Any guidance much appreciated! Thanks 😃 |
Beta Was this translation helpful? Give feedback.
-
Thanks all for weighing in! It seems like a positive change for most folks with only minor caveats, so we're going to move forward and merge. |
Beta Was this translation helpful? Give feedback.
-
Is it support
|
Beta Was this translation helpful? Give feedback.
-
About two years ago, we introduced binding tools to help reduce the boilerplate that came with using SwiftUI bindings in a feature. Due to some edge cases in that original design, we had to make them a little less nice, but the tools have more-or-less remained the same ever since.
They aren't without their faults:
The library does provide a tool for the problem of view state and view actions, but it’s difficult to wield and introduces more boilerplate than the tools intend to remove.
These shortcomings have been on our radar for a long time, and we want to provide a better solution before shipping 1.0. And even though WWDC23 has introduced new tools that largely eliminate these issues, we want to provide a solution that works for folks today, and for apps that can’t target iOS 17 and friends immediately.
Of all our many experiments to solve these problems, one stands ahead of the pack, so we’d like to present it today and gather a bit more feedback before merging a solution.
BindingViewStore
and@BindingViewState
We would like to introduce two more tools to our ever-growing menagerie of binding APIs in order to better support bindings with view state. They are the missing pieces that stand alongside
@BindingState
,BindingAction
, andBindingReducer
. Bindings with non-view state will work mostly as they did before with one improvement/caveat.But when your feature requires view state, there is now a much more straightforward path. Let’s say you have an existing feature that needs view state (perhaps it will be embedding a child feature that its view should not observe):
Your view should introduce a
ViewState
struct so that it only observes the bare essentials needed for the view to render.To do so with the new tools:
Introduce that
ViewState
struct to your view, marking all bindable properties with the@BindingViewState
property wrapper:Use the new overload of
WithViewStore
whereobserve
is handed aBindingViewStore
, which can be used to produce@BindingViewState
using.$propertyName
:Tip: We can extract this transform into helpers…
E.g. on
FormView.ViewState
:Or on
BindingStore<FormFeature.State>
directly:Access these bindings in the view using
viewStore.$propertyName
:Note that we have generally restored this
viewStore.$propertyName
syntax for deriving bindings, so you will be able to simplify your existing features:To recap, the two new types introduced to facilitate bindings and view state, and their respective roles:
@BindingViewState
: A property wrapper for producing bindings from view state. Note that we cannot simply use@Binding
because view state must generally be equatable, andBinding
does not conditionally conform to theEquatable
protocol (and probable should not).BindingViewStore
: A wrapper for producingBindingViewState
values from a store when building a view store.View actions
Our binding tools are also now more compatible with view actions.
As an example, consider the following domain with a sub-set of view-only actions:
With that defined, we can now point
WithViewStore
'ssend:
to the view case and access the bindings accordingly:A deprecation
The one deprecation and breaking change introduced by this PR is that you should no longer chain the
@BindingState
property wrapper to produce childBindingState
. This means you should not longer do:Instead a reducer can only listen to the firehose of changes:
If you must listen for more granular changes, move your@BindingState
annotation to the child properties, instead.Edit: The above was ill-advised, and the issue here is a bit more subtle. See this reply and thread for more info.
As a result of this change, though, we can recover a simplified view syntax we eliminated long ago:
More examples
The examples have all been updated. Here are a few:
Todo
feature of the Todos app: https://github.com/pointfreeco/swift-composable-architecture/blob/c73f5daed5dec36f7da5170ade74a5c2e838557d/Examples/Todos/Todos/Todo.swiftTodos
feature, which contains view state: https://github.com/pointfreeco/swift-composable-architecture/blob/c73f5daed5dec36f7da5170ade74a5c2e838557d/Examples/Todos/Todos/Todos.swiftLogin
feature of the Tic-Tac-Toe app uses both view state and view actions:Take it for a spin today!
We want your feedback, and we want to make sure this solution works in your existing code bases, please take it for a spin today:
binding-store
: If you’re on a recent, released version of TCA, point to thebinding-store
branch.prerelease/binding-store
: If you’re onprerelease/1.0
, point toprerelease/binding-store
.Beta Was this translation helpful? Give feedback.
All reactions