SOLID principles have a 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 out that each module/component/class must have a single responsibility. For our module, each deep link 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:
At the same time, each deep link 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 that can be extended without the need to modify it. To be more specific, when trying to add a new deep link type to an existing system, other deep links 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:
Now let’s create a manager class to allow points of entry for our deep links:
Using Applinkables as supported link types, allows us to conform to the open/closed principle where we can add new link types without breaking existing links.
We can create CustomerLink:
And, all we need to change, is modifying
link variable on
DeeplinkManager and add
CustomerLink() instance to it.
Liskov Substitution Principle (LSP)
Supporting the 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 to move
getLinkable the function to
AppLinkable. This will allow us to iterate links without knowing which link it is.
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 the 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 deep links will support opening from a URL known as a universal link on iOS.
To achieve it, we need to add a function in AppLinkable to check whether the link is satisfied by a payload.
Now, we need to implement a new
getLinkable payload function in our all links, whether it’s a supporting payload or not. This is violating ICP, right?
Thanks to the swift extension, we can actually make it optional for the links that do not need to implement this function. So by default, we can extend AppLinkable’s to return
nil for payload function:
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 a high-level component.
Let’s finalize our DeeplinkManager by implementing its routing functions by the following DIP.
We need our application to listen to deep links 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):
And let’s create
Now we have a routable for CustomerLink, we need to implement the 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 the route function for
As we have finalized CustomerLink with its routing, now, it’s time to confirm our
Finally, let’s implement
We created a DeepLinkManager class that manages deep link presentation and routing. We can add as many as deep links we need, without breaking our application (in this case AppDelegate). To keep it simple, I implemented deep link 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 the conforming listener (in this case AppDelegate) through its routable.
I hope the article could help you to clarify SOLID principles and their usage in real-life app development.
Please feel free to share your impressions in the comments.