iOS Screen Navigation Engine at Revolut
Ever heard about the Coordinator pattern? Good for you, this article isn’t about it. I’m here to open your eyes to Custom Navigation Engines just like the one we created on our Revolut Flow Engine.
First let’s talk about some traditional approaches for navigation before we get into our Flow engine:
Storyboard actions navigation
The most traditional way of doing navigation with storyboards is the one that’s triggered by an action. As you know, you can ctrl + drag from your button to reach the desired screen on your storyboard.
Segue driven navigation
Another traditional type of storyboard navigation are segues, by doing ctrl+drag from one view controller to another we create a connection of a certain type (push, modally, etc) and assign it an ID.
ViewController code driven navigation
The most traditional way outside storyboards, is to instantiate and push or present your view controllers programmatically.
Flow Engine
Now let’s bring out the big guns: the abstraction of navigation outside view controllers.
Why do we need to abstract navigation? Well, imagine you have a FoodDeliveryApp and in there you have a screen called ProductDetails that shows the information for a given product (Hamburger 🍔, fish and chips 🍟, etc) — but you want to call this screen and its dependencies from anywhere. Will it be enough to just instantiate the screen everywhere, with its dependencies and its actions? 🤯 of course not! 😅
Wait, what? Don’t worry, here’s some code:
When we want to call our ProductDetail screen and pass its business actions through dependency injection, we end up doing this:
Code looks clean to you? Well, kind of 😅. What happens when you need to show that product page from other view controllers and handle its actions? .showRelatedProduct
and .addToOrder
? Copy and paste this implementation? 😢 Of course not!
What if you could abstract this so you only call some magic method? Let’s say runProductDetailsFlow(productId:_)
with actions and dependencies of this screen abstracted in that layer? Well, without further ado:
Basically as the name says, it’s an engine (state machine) to handle a flow (set of steps). A carefully crafted state machine created by our own Revolut Legend Ilya Velilyaev. Let’s start with the backbone of it:
After having your FlowEngine
(feel free to copy and paste that file, and use it to create great flows in your apps 🚀), you can create Flows for your different business cases. Let’s go back to our FoodDelivery app example and create a flow to show ProductDetail
with its dependencies and business actions abstracted! 🎉
Once you create and define yourFlow
with its steps and initial State, you need to create aFlowPerformer
— the one who will actually execute these steps and their logic.
When you have your FlowPerformer
you can proceed to create your magic maker FlowRunner
— this is the one that allows you to call as one liner: flowRunner.runProductDetailFlow(...)
:
Finally from anywhere in your code (most preferably from other flows), you can call as many flows as you like while avoiding code duplication and have an easier TDD approach to your code.
That’s it, hope you enjoyed and that you start controlling, testing and abstracting your navigation in your apps 🚀