Earlier this year we devoted a series of episodes on the topic of “concise forms.” In them we explored how SwiftUI’s property wrappers make it super easy to build form-based UIs, mostly because there is very little code involved in establishing two-way bindings between your application’s state and various UI controls.
We contrasted this with how the Composable Architecture handles forms. Because the library adopts what is known as a “unidirectional data flow”, the only way to update state is to introduce explicit user actions for each UI control and then send them into the store. Unfortunately, for simple forms, this means introducing a lot more boilerplate than what we see in the vanilla SwiftUI version.
But, then we demonstrated two really interesting things. First, we showed that some of that boilerplate wasn’t necessary. By employing some advanced techniques in Swift, such as key paths and type erasure, we were able to make the Composable Architecture version of the form nearly as concise as the vanilla SwiftUI version.
And then we showed that once you start layering on advanced behavior onto the form, such as side effects, the brevity of the vanilla Swift version starts to break down. You are forced to leave the nice world of using syntax sugar for deriving bindings, and instead need to do things like construct bindings from scratch. On the other hand, the Composable Architecture really shines when adding this behavior because it’s perfectly situated for handling side effects.
Even though we accomplished something really nice by the end of that series of episodes, there is still room for improvement. One problem with our current solution is that it’s not particularly safe. If you make use of the tools we built in those episodes you essentially open up your entire state to mutation from the outside. This goes against the grain of one of the core tenets of the Composable Architecture, which is that mutations to state are only performed in the reducer when an action is sent. We are going to show how to fix this deficiency.
Further, we will make the binding tools we developed last time even more concise. In fact, it will compete with vanilla SwiftUI on a line-by-line basis, and in some ways it can be even more concise than vanilla SwiftUI.
So, let’s start by giving a quick overview of what we accomplished last time.