Managing state in your app is not an easy thing to do. Even in a relatively simple app there’s a lot of things going on. Users log in, data is being refreshed from multiple sources, errors start flying around… There’s a lot going on. We try to make our lives a bit easier by using delegation, callbacks, KVO… But there’s a simpler way π ReSwift is a ‘redux-like implementation of the unidirectional data flow’. In this article we’ll give you an intro to ReSwift and convert one of our existing apps to ReSwift.
Redux And Unidirectional Data Flow
Redux has its roots in the JavaScript world. It’s a state container. It was developed with the idea that apps have a state and they should run consistently across platforms and interfaces. It shouldn’t matter if you run your app on the backend, frontend, mobile apps or desktops, the only thing that changes is the interface. The state of the app is the same. This, in turn, meant that the apps that are written using Redux are easy to test.
Unidirectional Data Flow
There are only four things that you’ll be using when working with ReSwift. Those are: Actions, Store, Reducers and State. The beautiful thing about this is that the data always flows in one way, in the way we just described. An action triggers the reducer that’s called by the store. The reducer produces a new state that’s saved in the store. This makes it incredibly easy to debug your apps.
Take a look at it on the diagram:
Like we said before, we have four simple things to think about:
- ActionΒ – Is usually triggered from the UI (but doesn’t have to be) and it’s forwarded to the store.
- Store – Takes in an action and forwards it to the reducer which creates a new state. When the reducer creates a new state it’s broadcast to all the subscribers of the store.
- Reducer – Reducers will take in an action and a current state and produce a new state.
- State – Is the state of your app π
As you can see, it’s quite a simple concept, an ReSwift is a tiny library. It provides some markup protocols and has a few thousand lines of code. You could probably do your own Redux implementation if you wanted to π
Enough of the theory, let’s write some code…
The Example
There’s a simple app that I used previously to demo dependency injection. It’s connecting to the movie database and displays a list of popular movies. We are going to use this app in our example and convert it to ReSwift. It will allow you to see how ReSwift can fit into your existing apps.
The easiest way to get ReSwift is by using cocoapods, so add it to your podfile and ‘pod install’:
Now we need to think about the states our app can be in…
App State
Our apps have states and manage it during the lifetime of the app. With ReSwift we’ll be storing that state in a single object. You could argue that saving state in one object is a bit of an overkill, but, if you think about it, our apps already store the state. They just do it in the view controllers (or controllers).
Storing the app state in a centralised location makes it so much easier to manage the app. Storing all the state variables in a single data structure would be a bit disorganised. That’s why we can store states in substructures. Even though our example is quite simple, we’ll be storing our state in a substructure to illustrate a real-world use-case. What we’re going to store is the movies list state.
This is how our movies list state will look like:
As you can see, it’s pretty simple. We’re implementing a ‘StateType’ protocol, which is just a marker protocol. If you check the implementation, you’ll see that it’s empty.
The movies list state is a substate of the app. It will be stored in the ‘AppState’ structure, which is the main state of the app:
You can see where we’re going with this π Depending on your app, you can have many different substates in the main app state.
Reducer
The reducer has a simple job. It will take in an action and a current state and produce a new state. You app will have only one main reducer. But, usually, every different substate of your app will have its own reducer. In our case, our movie list state will have its own reducer:
You can see from the code that we’re creating a new ‘MoviesListState’ and based on the action, we’re setting properties of the state. This reducer will be used by the main reducer:
The main reducer is just creating a new instance of the ‘AppState’. You can see that we’re calling the movies list state reducer from it so we can get the proper movies list substate.
Store
The store is responsible for calling the reducers and holding the current state of the app. It will also notify all the subscribers about app state changes. We’ll keep the store as a global variable in our app:
We’ll keep it in the AppDelegate.
Actions
This is where the action is π You’ll have to think hard about what your app is really doing. In our case it’s simple. We’re getting movies, saving them and clearing them. So our actions are quite simple. This is also where our API calls will be. The action for getting movies looks like this:
As you can see, we’re doing our API call here. The function is returning a movies list action. Let’s take a moment to analyse what’s going on here. The function will return the ‘.getMovies’ action as soon as it’s called. The store will forward this action to the reducer that will set the movies list state to the ‘.loading’ state. Then, when the API call is finished and we have new movies, we dispatch a new action to the store that will forward it to the reducer and set the new app state.
View Controller
Our view controller will be firing some actions. We’ll only fire two actions, to load and to clear movies. We already saw how we can dispatch actions to the store from within another action. So this will be quite simple for us to do. Our view controller will also listen for the state changes in the store. Every time the store updates, we’ll update our UI.
These are our two actions that we’ll be dispatching:
The ‘.clearData’ actions is pretty simple. When we’re fetching movies we’re passing a function to the store that has a specific signature and produces an action. Dispatching any of these two actions will update the state of the store. Now we need to subscribe in order to receive those state updates.
Subscribing and unsubscribing is quite simple:
As you can see from the code snippet, when we subscribe to the store, we can actually select the substate that we want to subscribe to. This will reduce the number of callbacks that we’ll get and it will ensure that we only get updates that we’re interested in.
All that’s left to do is to implement the protocol:
Every time the state changes (and as soon as we subscribe) we’ll get a new state. The state that we’ll get will depend on the state that we subscribed to. In our implementation you can see that we’re simply switching over the enum to see what state the movie list is currently in and updating the UI accordingly. One great thing to note here is, it doesn’t matter who updated the state we’ll get the new state. Any other controller (or view controller) could have dispatched an action to the store and we would have picked up the change simply because we subscribed to it.
And that’s it… We have successfully switched our project to ReSwift π
Conclusion
In this article we’ve went over the basics of setting up and using ReSwift. You can see how easy it was to decouple your UI from the logic. This also means that the code that we’ve written is highly testable because it’s not coupled with UI. We’ve only scratched the surface here and there’s so much to learn.
If you want to learn more about ReSwift you can head over to their official repo and check it out. You can find loads of good examples there. You can also find the example app from this post in the GitLab repo. As well as all the code snippets.
I hope you’ve learned something new today and, as usual….
Have a nice day π
~D;