SwiftUI and Redux — Clean Code and Small, Independent Components
How to use the Redux library and integrate it in your SwiftUI app
Redux is really cool, SwiftUI is even cooler, and having both in the same app is heaven on earth. So, yet again, I’ll try to convince you that this is the best (or worst) SwiftUI app architecture.
A very naive implementation of Redux using Combine BindableObject to serve as an example In this little guide, I'll…
During the past few weeks, I’ve evolved my small, naive Redux SwiftUI library. I’ve added a readme which barely explains how to make it work in your app, but I have a working example anyway.
More importantly, I’ve added
I can’t thank Alexey Demedeckiy enough, as the new additions are heavily inspired by his earlier work on SwiftUI and Redux. I’ve added it to my library and refactored part of MovieSwiftUI with it. It works wonders with my pre-existing Redux architecture.
I won’t talk about Redux concepts in this article, this is a tutorial on how to use the library and integrate it into your SwiftUI app.
It’ll, of course, give you an overview of how it all works together but it’s more about components architecture. For explanations, you can refer to my previous article linked in the first paragraph.
That said, let’s dig into the code.
Any SwiftUI project will have a
SceneDelegate — this is a new class since iOS 13/macOS 10.15, which manages the windows instances and presentation of your app.
This is basically the new entry point and where you’ll initialize your root-view hierarchy.
In our case, we’ll also create our Redux initial
state and provide it to our
store, then wrap it using
StoreProvider around our root view.
Behind the scenes,
StoreProvider is just a fancy name for a SwiftUI view which will set our
store as an
environmentObject of our content view.
It could have been done with
HomeView().environmentObject(store), but having this wrapper means that it’s safe for me to evolve the underlying API without touching the code.
With SwiftUI, you need to inject your
environmentObject in the view hierarchy once only, if it’s done at the root, as it is here. There is still a bug, for now, with
.sheet where it’s not correctly passed but it should be resolved in the next few beta’s.
Creating a Reactive Component
Let’s now see how we can make a component, or a view, that reacts to change in the
action (to fetch data or because the user triggered something in your app and it needs to update its state), your
state will change. And, as properties are updated in the store, your component should be updated according to what it needs.
We’ll take a reusable component I use in a lot of places in the app as an example:
Let me show you two images:
This is the context menu you’ll get when you force-touch on any actor/director/etc. in MovieSwiftUI, so you can add this person to your fan club.
It’s a self-contained view; the only thing it needs is a person’s ID and from that, it can manage its UI state and dispatch action as the user taps on it.
The first thing we need to do is make it conform to
ConnectedView is a protocol provided by SwiftUIFlux, and, by implementing the new
body function, it will wrap your view around a view that responds to your
AppState change because it’s injected as an
The second thing we need to define is
Props. This is very simple; we only need one
Bool to know if this person is in our fan club or not.
Then, we have our
people ID constant, which is passed when the view is constructed externally, like this:
Now the interesting part, the
map(state:dispatch:) is a function you need to implement when you want to conform to
This is the only place in your view where you’ll have access to your
AppState. Any data, properties, and computation you need to derivate from your
AppState to make your state properties ready for the UI should be done here.
It’ll be called just before the new
body(props:) function is called, so just before the view is actually diffed or rendered.
In our case, we just need a
Binding property wrapper allows us to provide a value derived from our state and to execute an action if it’s mutated anywhere in our component.
It works because the
Binding property wrapper exposes an initializer where you can pass functions as parameters for
setValue, and this is what we’re doing here.
So, all the logic for this component is wrapped here and then the actual state mutation is done in the reducer.
But, for the UI part of the component, all it will see and have access to is a nice
body(props:) function is called and allows you to return your component UI with passed-view properties. And, as you can see in our case here, it’s just a
Button with an
HStack which provide an
Image and a
As we touch the
Button, we trigger our
action by toggling the
boolean value of our
That’s it, now, wherever you want a
ContextMenu to allow the user to add an actor to its Fan Club, it’s a one-liner to add this component to the desired view. And it’s a small, beautifully wrapped component.
It’s fully testable in both states by injecting external props and it is easy to preview in the Xcode canvas.
This was a simple component. Below is the code for a more complicated one; this is also a
ContextMenu, but the one for movies.
I provided the full code because it exposes a few more things you can do with props.
Here, I pass functions to do my actions in the
map(state:dispatch:) function. I could have done this with a
Binding<Bool> also, but for this example’s purpose, I did it another way.
I also split the view body into multiple functions for easier code reading, so as you can see, you’re free to forward your props to wherever you need.
I hope this article will shine some light on SwiftUI + Redux!