App Navigation Routing Algorithm 🌳
Recently I’ve been experimenting with ReSwift and Katana and I created my first OSS library in Swift: KatanaRouter 😎
Katana and ReSwift are based on Redux, and the idea is simple: keep all of your state in one structure. The thing is, it also should contain the navigation state, that’s why ReSwift-Router was born.
I was inspired by ReSwift-Router to create a similar library for Katana, but I’ve taken another approach.
It has lead me to creating an algorithm that can be used in any routing really, not just Redux-style architecture.
Problem 🤔
An app navigation state is a tree:
UITabBarController(active)
+ + +
+-----------+ | +------------+
| | |
| + |
+ +
Tab One(active) Tab Two Tab Three
+ + +
| | |
| | |
| + |
+ +
ChildVc(active) ChildVc ChildVc
It’s simple enough to change that tree if you want to push a destination, or pop one. It gets much more complicated if you want to do more complex operations 😨
That’s why, for the needs of KatanaRouter I created a diffing algorithm for trees that represent a navigation state.
Solution 💡
Let’s say that we have navigation state tree A and tree B. The task is to return all the important differences in these trees. I’ve distinguished 4 different actions:
• Push - Singular push action happened somewhere in the tree.
• Pop - Singular pop action happened somewher in the tree.
• Change - A more complex action. At least two singular pop/push actions.
• ChangeActiveDestination - Called when there’s a new destination set to active e.g. child in a tab controller.
So, I had to create an algorithm that accepts two trees and returns an array of these actions. What’s very important is the order: you have to run all the UI transitions in the correct order, otherwise it doesn’t make sense. The easiest rule is that first you have to pop, then you have to push, right? But there’s a lot more:
- First we have to traverse through inactive nodes and finish with active last.
- You have to return all the pop actions in post order
- You have to return all pushes in pre order
- Any “Change” actions, which contain pops + pushes, you have to return in the order of the push actions.
So I wrote a NavigationTreeDiff
class that does exactly this.
Here’s the whole class if you want to take a look.
Usability 🛠
As I already mentioned, this algorithm can be used in any app routing, not only Redux-style Katana, or ReSwift.
It’s really simple, you just have to create a navigation tree, manipulate it any way you want, get the differences actions, go through these actions and apply them in your UI layer.
This is not limited to UIViewController
s. You can as easily model a UIViewController
destination which has children UIView
destinations in a UIScrollView
and freely change them around.
That’s it! Please let me know if you have any feedback or ideas, I’d love to hear it! 🖖🏻