Better deep links handling in modular iOS App
Deep links is powerful and underestimated mechanism for routing from outside of your app. But what if we could use it for in app routing? Routing is one of the most complicated problems iOS engineers (and Android) face in every day live.
Today i will try to suggest one more way, we could solve this problem.
Deep links
Firstly let’s find out what deep link is. Deep link is a link that points to some feature in your app. When user taps some link, operating system can resolve that app can handle it without opening browser. There’re tons of tutorials which help you to set up your app and use this mechanism, so we’ll miss this topic now. But if you are not familiar with this topic, do not worry, full source code is available on Github
Let’s start from problem formulation
I think that most of iOS developers use deep links in their apps. Usually it turns into a huge if else statement with string parsing from url. Such a code is hard to maintain, scale and much more important — test. Let’s imagine how common AppDelegate file with few deep links looks like.
In the best scenario, all this transition stuff moves to another entity like overloaded router. Further, the problem can be aggravated by the existancy of the modular architecture in the application. Then deep links
generate big code dependency, in some case programmers decide to move deep links implementation on the highest level of app, in target app. In this article we will try to implement mechanism which will let handle deep links much more flexible and independent.
What do we want to achive
— Incapsulate logic of one transition into one independent entity.
— Try to avoid huge if else statements
— Enable deep links testing
— Make deep links suitable for modular architecture
Modules
Let’s start from the most irrelevant topic of this article and implement primitive modular architecture
We have just created new protocol called FeatureModule and added there two required methods which we will need later. All application modules will implement this protocol. Next we have created new BookmarksModule and implemented required methods. That’s it for modules, it’s clear that it’s not completed modular architecture, but that’s enough for our example.
Know head back to deep links
Endpoint
For the beginnig, i suggest a new definition, endpoint — object performs one transition.
isAvailable
— variable for some kind of transition availability check. May be you are using Firebase remote config and want to disable some transition. Or endpoint can be performed after some app events.
startTransition(in navigation: UINavigationController?)
— as it clear from function name, transition should be performed within this method.
Simple endpoint definition can look like this
RoutingAssembly
After module definition we should give it possibility to provide endpoints it can handle. Let’s create protocol called RoutingAssembly
with necessary methods. Module conforms this protocol and after it can be used as deep links assembly
Here we can choose within two different ways. The first method — assembly provides deep link by request, after transition started. The second one — assembly provides all deep link which it can handle. All this methods have pros and cons. First method gives your more flexibility if you are using lazy dynamic library loading. It gives module much more time to prepare internal entities. Second one provides all deep link on start of app and router wouldn’t make extra calls to module, which can be overloaded with business logic. In this article we will cover first method.
Handling endpoint availability
As you probably remember, we have computed variable isAvailable
which returns Bool
in RoutingAssembly
protocol. For this simple example let’s create struct
Then we should create variable for this struct in our module.
and finally create method for availability checking
After this we can edit isAvailable
in Endpoint
Router
Router is main part of this mechanism, so let’s dive into it.
func append(assembly: RoutingAssembly)
— method for passing deep links assemblies to router, which was covered few paragraphs before.
startTransition
— method to make router prepare transition and start it
set(navigation: UINavigationController)
— sets navigation controller to push UIViewController
from selected transition
and finally set(errorHandler: RoutingErrorHandlerProtocol)
— methods sets entity that handles all errors during transition
Last three methods are very straightforward, they only sets passed variables into internal stored variables or adds assembly to stored array. startTransition
method is a little bit more complicated, so let’s dive into it.
Applying knew knowledge
Know, after all preparations, we can rewrite AppDelegate class and get some this like this.
When application receives deep link, we should handle it.
And finally, start transition
Final results
Full source code is available on Github