A Flexible Routing Approach in an iOS App
At Rosberry we’ve given up on using storyboards, except the Launch Screen, of course, and configure all layout and transition logic in code. In order to understand our reasons — read the Life without Interface Builder written by ’s team, I hope you will find this post useful.
In this article, I’m going to introduce a new approach to routing between view controllers. We’ll start with a problem and step by step will come to a final decision. Enjoy reading!
Don’t miss a chance to subscribe to my telegram channel! Be the first to know interesting news, articles from the iOS World!
Dive into the problem
Let’s figure out the problem using a specific example. For instance, we’re going to build a social app with profiles, list of friends, chats and so on. Definitely, we can notice that it’s necessary to show a user’s profile from many controllers and it would be great to implement this logic once and reuse it. DRY, we remember you! We can’t achieve it by using storyboards, you may imagine how it’d look within a storyboard — weeeeb. 😬
At present, we’re using the MVVM+Router architecture. ViewModel says to Router that a transition to another module is needed and the router does it. In our case Router just takes all the transition logic in order not to come to the massive view controller (or view model). If it looks unclear to you at the beginning, please don’t worry! I’m going to explain this solution in a plain way, so it will be easily adapted to the simple MVC as well.
1. At first it would possibly look like an obvious solution is adding an extension to ViewController:
and it works as needed — write once, then reuse. But it’ll become messy when many transitions come. Xcode autocompletion doesn’t work well I know, but sometimes it can show you a lot of unnecessary methods. Even if you don’t want to show a profile from this screen, it’ll be there. So, go further and try to improve this one.
2. Rather than writing an extension to ViewController and have tons of methods in one place, let’s implement every route in a separated Protocol and use the awesome feature of swift — protocol extension.
Now, this approach is more flexible — we can extend a view controller with needed routes only (avoid tons of methods), just add a route to the controller’s inheritance. 🎉
3. But of course there’re some improvements:
- What if we want to have a modal transition to the profile controller from all places except one? (it’s a rare case, but anyway).
- Or more importantly — If I change the presentation type, I should change the type of a dismiss transition as well (present / dismiss).
We don’t have a chance to configure it for now, so it’s time to implement a Transition abstraction with few implementations — ModalTransition and PushTransition:
I reduced a bit the implementation logic of the ModalTransition just for simplicity. A full version is available in Github.
and the similar reduced logic of the PushTransition:
You definitely have noticed the Animator object, so it’s a simple protocol for custom transitions:
As I said earlier about a massive view controller, let’s add an object that comprises the whole routing logic and is contained as a property in a view controller. We meet Router — a base class for all future routers. 🎉
Please, spend a bit time to understand this code. This class contains two methods for opening and closing, a reference to a view controller and an
openTransition in order to know how to close this module.
Now using this new class let’s update our ProfileRoute:
You can see that the default transition is modal and in
openProfile method we generate new module and open it (of course it’s better to use a builder or factory to generate a module). Also, pay attention to a
profileTransition is saved to this variable in order to have one instance.
The next step is updating the Friends module:
We’ve created the FriendsRouter and added the needed routes via typealias. Here’s where magic happens! We use protocol composition (&) to add more routes and protocol’s extension to use a default implementation for routing. 😎
The last step in this story is how easily and nicely a close transition can be achieved. If you recall ProfileRouter, there we’ve configured
openTransition and now we can take advantage of it.
I created a Profile module with only one route — close and when a user clicks a close button, we use the same transition type to close this module.
If necessary to change a transition type, just change it in the protocol extension of ProfileRoute and this code will continue working without any changes! Awesome, isn’t it?
At the end, I want to say that this Routing approach can be simply adapted to MVC, VIPER, MVVM architectures and even if you use Coordinators, they can work together. I’m trying my best to improve this solution and will listen to your bits of advice with pleasure!
For those who are interested in this solution, I prepared the example with few modules and different transition between them to understand it more deeply. Download and play!