One of the most painful issues in our job is caused due to state management and have more than 1 source of truth, and today is what we are going to talk about in the SwiftUI context (also applicable to UIKit) and why organizing and disciplining the data flow is one important skill to have when choosing the architecture and the design for a specific screen.
1 Source of truth is always better than more
Let's start with the problem
So, why are we talking about this issue in the first place? and what makes it a big problem when it's made to make our life easier?
First thing first, let's understand what we are talking about today, what we are talking today is Data Flows, where data originates, how it traverse starting from the UI Interaction, to the project's backend until it returns to the UI Feedback to the user
So we have two tales to compare, the tale of the Uni-directional Flow and the Bi-directional Flow
At first, when Component-Based Architectures were made, there was a problem, a problem about data and how do Components gets updated in the web-perspective, Angular was a good example for this (Yep, don't worry we are gonna talk about SwiftUI soon 😉)
So, we had an approach of, why don't we make components communicate with each other? like in here
So Component A can talk with B, B with C, and so on.
Of course, if you are talking from a perspective of Components being Synced, that doesn't mean all components work in harmony, and threading and race-conditions start to become a problem and guess what, these kind of problems are not really cool to work with
The case here is similar to a boat in the middle of the sea with no captain, nobody in charge to tell who does what, and giving orders, so the result, the crew starts to fight, and eventually get drown.
So what we should do here is, hire an orchestrate, a maestro to regulate work
Back in December 2019, I attended a session by Peter Mosaad in SwiftCairo, He was the Engineering Manager @ DeliveryHero back then, and he was shedding lights at one of the most problems back then that I had no idea about back in that day, they had the same issue where there were multiple components at the home screen that we @ elmenus had as well.
The problem was having multiple complex components in one screen, each component has its own state management and logic, the solution as a concept was easy, don't give the component ownership to handle its own state, allow it to channel the state it wants to mutate itself to, to the parent, and the parent 'orchestrate' the change with all the sub-components, and this is exactly what Angular 2.0 made, which made it fix a really big problem in state management.
But is that it? If you've been reading what am suggesting until now, you will find that you are basically giving the parent way more responsibilities than it should, and in the end, you will find that you making the parent a blob, and we don't want to add more pile of issues to our already-is big issue.
Enters The Redux
Like many design patterns and architectures that encourage the Uni-directional Data flow, The redux, ReSwift, MVI, or however you want to call it, is a good approach in Declarative UI context, like in SwiftUI.
So what makes this approach Unique?
let's first see the basic overview of the approach
So the first thing you see is, the arrows' directions are uniform, they don't go back (making it bi-directional)
Next thing is, it utilizes reactive programming, like Combine, RxSwift, and so on...
Which in return, allows for an easier way of managing the state since they are all stored in the Store, and they don't get mutated unless action is made and took upon by the reducer.
So, in basic terms of a really complex project, this is how Redux handles stuff
Now comes The million $$ questions in the software industry, The when
When to Use Redux?
When you have a centralized place that handles the states of the app, this gives lots of benefits, most are direct, and some comes as a bonus for having this, let's focus on direct gains from having Redux usage in your app
- Predictability You pass immutable states and data across the app, nothing gets mutated, you just generate input, and that input gives you a constant output, so you are certain that this will happen
- Testability because the architecture implies code as pure functions, which are small units of logic that allow for the maximum capability of testing (you give input, u expect output with little-to-no side effects what more can you ask for?)
- Performance due to the nature of reactive programming, if you have multiple views that are connected to the store, the store can pick who needs update and who don't, which enables you to update only those in need of update rather than update the whole view
- Easier Debugging For debugging, all you can do is find out the history of the state, how it got created, and more importantly, how it affected the UI, when you have all of this displayed to you in a pretty way all inside of Xcode, you can't go wrong much, and with the right tools and best practices that we will discuss in this series.
For the non-direct gains
- Centralized source of truth Having a centralized source of truth makes it easy to manage and maintain that source, and testing it greatly adds up to the overall quality of the project
- Easy Replacement or Mocking Another reason for this is that If you want to mock the states in a testing environment or maybe even a staging environment, you can do so, allowing for great coverage for edge-cases
When to NOT use Redux?
- Over-engineering Like anything in life, overdoing stuff is bad, same goes for engineering, if you have a basic app that just shows data and not a lot of state handling, throwing Redux into your app might be a bit of an overkill and is considered over-engineering.
- Data is already coming from 1 source only without any flow-changing requirements internally in the UI The main problem that Redux is solving here is multiple state mutation and handling, so if that problem is already not there, then why would we use a solution to a non-existing problem
Where to go from here?
So now that we've talked about Uni-directional and Bi-directional, and one of the good approaches in the books to use with SwiftUI like Redux
let's dive deeper into the next post!