Many, many weeks ago we built a moderately complex application in SwiftUI from first principles, using only what SwiftUI gave us out of the box (part 1, part 2, part 3). We were able to get really far, really quickly, but we noticed a few problems along the way. We distilled what we observed into 5 main problems that we think are crucially important for any application architecture to solve:
The basic units of the architecture should be expressible as simple value types.
Mutations to app state should be done in a single, consistent way, and the units of mutation and observation should be expressed in a way that is composable.
The architecture should be modular, that is you should literally be able to put many units of your application into their own Swift module so that they are fully separated from everything else while still having the ability to be glued together.
The architecture should tell you exactly where and how to execute side effects.
And finally, the architecture should describe how one tests the various components, and ideally a minimal amount of setup work is needed to write these tests.
SwiftUI gets us really close to solving some of these, but didn’t get us all the way there.
This inspired us to embark on exploring an architecture that gave very strong opinions on how each of these problems should be solved, and we’ve fully solved 4 of the 5 problems.
State and actions are modeled as value types
Mutations are expressed as reducer functions, which are super composable.
Observation to state changes are expressed using a store type, which is also composable and allows us to split all of the screens in our app into their own Swift modules that can be run on their own.
And most recently we finally showed how side effects can be modeled in this architecture.
That leaves one last problem to solve, and perhaps the most important: testing. We claim that this architecture is super testable. Pretty much every facet of this architecture can be tested, and it requires very little setup work to write your first test. We will also be able to unlock lots of new ways of testing that are very difficult to achieve without a cohesive and pervasive architecture in your application.
So, let’s start by reminding ourselves what we have been building for the past many weeks.