Unlock This Episode
Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.
Introduction
We have now spent many, many weeks building up our Composable Architecture from first principles. Its core design was motivated by trying to solve five problems that we found crucial for any application architecture to solve.
We then refined this design by addressing a couple memory leaks and a potential performance concern with how our architecture originally interfaced with SwiftUI. Tackling the latter issue provided us with an opportunity to enhance our architecture to be more adaptable to various situations, which allowed us to share core business logic across many platforms while refining the way each platform interacts with that shared logic.
We still have many, many things we want to explore in our architecture, but with these leaks and performance concerns addressed, we think it’s time to package things up to use in our applications. We could maybe even share it with the world as an open source project.
But before we do, we feel there is still some room for improvement. For one thing, we haven’t spent a ton of time on the ergonomics of the Composable Architecture. The core library is pretty small: less than a couple hundred lines of code. But even with such little surface area, I think we can take inspiration from earlier episodes of Point-Free as well as new Swift features to smooth out some of the rough edges around using these APIs.
Subscribe to Point-Free
Access this episode, plus all past and future episodes when you become a subscriber.
Already a subscriber? Log in
Exercises
Now that the
Reducer
type is a proper struct we can provide specialized initializes for common use cases. For example, it often happens that a reducer doesn’t need to do any side effects, and therefore it doesn’t need to return anything and its environment could beVoid
. Create an initializer onReducer
for whenEnvironment == Void
that allows us to create a reducer without having to worry about side effects.Solution
extension Reducer where Environment == Void { init(_ reducer: @escaping (inout Value, Action) -> Void) { self.reducer = { state, action, _ in reducer(&state, action) return [] } } }
Continuing the previous exercise, there is another form of reducer that some may like more than our current shape. Right now we have immediate access to the environment in the reducer, which means we technically could invoke the effects right there in the reducer. We should never do that, but it’s technically possible.
There is a slight alteration we can make to the reducer so that it is not handed an environment, but instead it will return a function that takes an environment and then returns effects. Create a static function on
Reducer
calledstrict
that allows one to create reducers from that shape.Solution
extension Reducer { static func strict( _ reducer: @escaping (inout Value, Action) -> (Environment) -> [Effect<Action>] ) -> Reducer { .init { value, action, environment in reducer(&value, action)(environment) } } }