We’ve finally fully extracted the most complex, asynchronous side effect in our application to work in our architecture to work exactly as it did before. There were a few bumps along the way, but we were able to address every single one of them.
This effect was definitely more complicated than the others, for two reasons:
First, this effect was bundled up with the idea of showing and dismissing alerts, which is something we hadn’t previously considered in our architecture. Solving it required us to consider what it means to extract out local state associated with the alert presentation, how to manage bindings, dismissal, and so on. Most of the bugs were around that, so in the future we’ll explore better means of interfacing with SwiftUI APIs.
Second, the effect was asynchronous! It’s just inherently more complex than a synchronous effect. We needed to take into account a threading issue, though it’s not a fault of the architecture and is an issue that anyone extracting logic from a view to an observable object would encounter.
We now have the type of our effect: that Parallel-like shape, where you get to hand off a function to someone else, where they get to do their work and invoke that function when it’s ready. And we have the shape of our reducer, which can return any number of effects in an array, where the results can be fed back into the store. And it was cool to see that we were able to, once again, do an async functional refactoring and have everything just work in the end.
We were also able to embrace the idea of “unidirectional data flow.” Even with the complications that effects and asynchronicity introduce, we’re still able to reason about how data flows through the application, because effects can only mutate our app’s state through actions send back through the store. The store is the single entryway for mutations. This is the basic version of the story for effects in our architecture.