My take on The Composable Architecture
iOS app architecture with composability and testability in mind
The Composable Architecture or TCA for short is a library to build application in a consistent and understandable manner, with composition, testing, and ergonomics in mind. This library is made by Point-Free to apply the concepts of functional programming in a practical way.
Context: Typical iOS Architecture
In the world of iOS development, there’s a few 4 most commonly used patterns in architecting an iOS application. Most notably MVC, MVP, MVVM, and VIPER. The details of these architecture is beyond the scope of this article. All these architecture has their own advantages and drawbacks. For example, MVC is simple but misusing it will cause a Massive View Controller. VIPER provides a good testability but it’ll generate much boilerplate code. At the end of the day, architectural decisions are based on the combination of use case, complexity, and many other variables. So there’s no such thing as the silver bullet of iOS architecture.
As the new guy in the iOS architecture neighborhood, TCA claims to provide:
- Consistency: degree of firmness, density, viscosity, or resistance to movement or separation of constituent particles
- Composition: a product of mixing or combining various elements or ingredients
- Ergonomics: a product of mixing or combining various elements or ingredients
In this blog, we’ll test whether TCA fulfills what it claims to do or not.
We’ll create a song reader app that have the capability to add songs to favorites and see all the favorites we have. All the code snippet in this blog is taken from my Lagu Sion implementation. For more details, please go here.
To install this library, generate a new iOS application in your Xcode and add TCA to your Swift Package Dependency. More info, head here.
Building the app
In this blog, I’ll build the app from the smallest component possible and work ourselves up and wire everything up.
Modeling Song: Simple, we can make a struct that contains all the fields needed. In this case we need to generate the
Modeling Action to be made at on Song View: Simple, just use Swift’s enum to enumerate all the possible action.
Modeling what will happen when action appears: Here, we start to use TCA’s first feature: Reducer. TCA will receive all the action that the views will trigger and after that, the action will be processed through the reducer. In TCA, reducer is the place where logic happens. State mutations, logical operation, and action handling is happening in the reducer.
Implement how the Song View will look like: Simple, we create a view just like other SwiftUI views, but at the end, we’ll wire up the view with the state through a concept called
Previews: Since we haven’t wire up the view to any entry point, we can use SwiftUI previews to see how our view implementation is going.
Finally, testing the implementation
For now, let’s take a break from coding and look back what we have made here. First, we have modeled the state of the Song View and actions. Next the reducer and the view. TCA provides us with a framework to pass triggers from view to the reducer through
Store . With store, developer can make SwiftUI previews easily. This checks the first claim that TCA is ergonomic.
Store concept also has it’s corresponding ergonomic component in testing named
TestStore . This component enable us to intercept the actions triggered, sequence of actions, or even checks the state after every action sent or received. This checks the second claim that TCA is designed with testing in mind.
Cool, so far we have covered how TCA is ergonomic and designed with testing in mind. Let’s go to the next part of the implementation to prove the rest TCA claims.
Modeling Main List View: In the list view state, we store two lists. The list of songs, and favorite songs.
Modeling the Main List View Actions: Here, we’re going to use our previously made
songReducer for each song we have in the main list.
Let’s take a break and see what we did. First, we model the
MainState just as we did on
SongViewState . After that, we create the model of
mainReducer . This shows that building component in TCA is consistent. Every layer of the implementation follows the same consistent pattern. This checks the consistency claim.
Next, TCA also gave us the tools we need to reuse and compose the
songReducer in the
mainReducer . As we know, the main state, composes song state directly or indirectly. If this happens, we can always compose any state with many other smaller states and we can still have the visibility of anything that happens in the child component. This checks the composability claim.
Last experiment I’d like to show is to fast forward on how we’re going to wire multiple sibling states to the root state.
Assume we have implemented the
Model App State: To wire up all the sibling states to single state, we have to define how are the
AppState behave when there’s a change in
AppState or if there’s any changes in the child states.
Model Action and Reducer:
Let’s stop and review again. In this section, we made the state, actions, and reducer. But if we look closely,
appReducer is doing nothing but composing all the child reducers and passing its respective states and actions using
pullback. This ultimately checks again the composability claim of TCA.
What I love
As we can see from the experiment, TCA delivers what it promised. Consistency, Composability, Testing, and Ergonomic. Personally, my favorite thing in TCA is how we can build mini components and wire it up to the chain until it has reaches the root/single source of truth. This ensures data consistency throughout screen and states.
What I don’t love
Right now, there’s no valid proof that this architecture is used in a huge app with high complexity. So we still have no idea on the extent of this architecture.
Where should I go next?
If you’re interested in how this library is designed, go to Composable Architecture series. If you’re into functional programming, especially in Swift, check out Point-Free’s site. They cover a lot of interesting ideas of functional programming specifically in Swift. If need more example of a working app using TCA, visit TCA’s GitHub examples.