Demystifying SOLID principles in Swift with a Deeplink Manager

Rashid Ramazanov
MobvenLab Eng
Published in
4 min readJul 20, 2021

--

SOLID principles have huge impact on building testable, maintainable and readable code and systems. Each of its letters stands for a principle and applying them all in a system or module brings advantages.

In this article, I’m going to demonstrate how we built a Deeplink Manager module based on the principles one-by-one.

Single Responsibility Principle (SRP)

This principle points that each module/component/class must have a single responsibility. For our module, each deeplink should have its own `single` responsibility for parsing a URL to the expected type. To achieve so, let’s create a swift struct for a link:

NotificationsLink

At the same time, each deeplink should have a responsible router for routing purposes. We will come to it later.

Open/Closed Principle (OCP)

Modules must be open for extensions, but closed to modifications

Meaning the system must depend on features which can be extended without need to modify it. To be more specific, when trying to add a new deeplink type to existing system, other deeplinks and their implementation should not be affected.

And how to achieve that? Links need to conform to a type which is a protocol in Swift:

AppLinkable protocol

Now let’s create a manager class to allow point of entry for our deeplinks:

Using Applinkables as supported link types, allows us to conform to open/closed principle where we can add new link types without breaking existing links.

We can create CustomerLink:

CustomerLink conforming to AppLinkable

And, all we need to change, is modifying link variable on DeeplinkManager and add CustomerLink() instance to it.

Liskov Substitution Principle (LSP)

Supporting open/closed principle, LSP states that objects of a superclass shall be replaceable with objects of its subclasses without breaking the application.

To do so, we need move getLinkable function to AppLinkable. This will allow us to iterate links without knowing which link it is.

AppLinkable with getLinkable function

Now adding function getLinkable on DeeplinkManager allowing us to iterate through available links and replace them with the value returned by AppLinkable`s getLinkable function to receive the link with parameters if needed.

Interface Segregation Principle (ISP)

The ISP states that no client should be forced to depend on methods it does not use.

So far, we know that all links have to implement getLinkable function provided by AppLinkable. Diving deep into our DeepLinkManager, we receive a requirement stating that some notifications can with opened from a push notification payload which is [AnyHashable: Any] type, tough all deeplinks will support opening from a url known as universal link on iOS.

To achieve it, we need to add a function in AppLinkable to check whether link is satisfied by a payload.

AppLinkable with payload

Now, we need to implement new getLinkable with payload function in our all links, whether it’s supporting payload or not. This is violating ICP, right?

Thanks to swift extension, we can actually make it optional for the links those do not need to implement this function. So by default, we can extend AppLinkable’s to return nil for payload function:

AppLinkable extension for getLinkable(payload:)

Dependency Inversion Principle (DIP)

High-level components should not depend on low-level components or modules and low-level components should depend on abstraction, not high-level component.

Let’s finalize our DeeplinkManager by implementing its routing functions by following DIP.

We need our application to listen to deeplinks when they need to be routed. In our application we need to know which link should be navigated without knowing its type. At the same time, we need to know what the link is to route to the related link detail.Let’s create AppLinkRoutable (following OCP):

AppLinkRoutable protocol

And let’s create AppLinkRoutable for CustomerLink

CustomerLinkRoutable

Now we have a routable for CustomerLink, we need to implement route function. Following LSP, let’s add a route function to AppLinkable, assuming each link must have a routing logic. Final AppLinkable will be:

Now we need to implement route function for CustomerLink.

As we have finalized CustomerLink with its routing, now, it’s time to conform our AppDelegate toCustomerLinkRoutable.

Finally, let’s implement DeepLinkManager.getLinkable(payload:):

Conclusion

We created a DeepLinkManager class which manages deeplink presentation and routing. We can add as much as deeplinks we need, without breaking our application (in this case AppDelegate). To keep it simple, I implemented deeplink routing on AppDelegate. AppDelegate is agnostic about the link it received, though it has routing implementation for each link through AppLinkRoutable conformance. Each link is responsible for calling conforming listener (in this case AppDelegate) through its routable.

I hope the article could help you to clarify SOLID principles and their usages on real life app development.

Please feel free to share your impressions on comments.

--

--