How to use the Redux library and integrate it in your SwiftUI app

Thomas Ricouard
Jul 26 · 6 min read
Photo by Crew on Unsplash

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.

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 StoreProvider, StoreConnector, and ConnectedView.

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 AppState.

As you’ll dispatch 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: PeopleContextMenu.

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.

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 @EnvironmentObject.

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 ConnectedView.

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<Bool>. The 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 getValue and 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 props.isInFanClub boolean.

Then, the 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 Text.

As we touch the Button, we trigger our action by toggling the boolean value of our props.isInFanClub.

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!

Better Programming

Advice for programmers.

Thomas Ricouard

Written by

📱 🚀 🇫🇷 [Entrepreneur, iOS/Mac & Web dev] | Now @glose 📖 | Past @google 🔍 | Co-founded few companies before, a movies 🎥 app and smart browser one.

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade