How to Decouple Dependencies in Swift / iOS Projects
If you want to create a modular system for your code, managing dependencies can become an issue. It’s a key point of your design. It’s crucial to decouple dependencies when developing Swift / iOS projects. Your code will be more readable, reliable, testable and of course modular. In this article, we’ll look at how to decouple dependencies in an iOS project.
What is a dependency anyway?
Dependency is what makes your specific code blocks dependent on other code blocks. So, your code depends on another part of your project to work properly. These dependencies can be 3rd party libraries/frameworks, manager classes, some modules you create (SPM), etc.
How to make our code independent of other parts of the code base?
As SOLID’s last principle “Dependency Inversion” suggest, “Classes should depend upon interfaces or abstract classes instead of concrete classes and functions.” So, the key is Abstraction here.
In Swift, we can define and confirm a Protocol to make a class abstract. This hides the target class’s implementation details and logic from other classes. Other classes only now what our class is capable of.
Sample Project
We will develop a sample project to make the topic we have covered in this article more detailed and understandable. You can find the source code here.

Project’s Folder Structure
You’ll find two folders named “Coupled” and “Decoupled”. These determine the 2 stages of the project as their names explain. Under the “Decoupled” folder you will see “ProtocolBases” and “AdapterBased” folders. These contain 2 different approaches to the decoupling dependencies.
Coupled Stage
Now we are demonstrating two classes that are tightly coupled with each other. How are they tightly coupled in the first place? Well, the API class is initialized and used inside a ViewController directly. That means they are referencing each other and our ViewController is not able to work with API class at different modules. ViewController is knowing what exactly the implementation of the API class and we do not want it.

A API class that initialized by a ViewController directly.

A ViewController that initializes a API class inside itself.
What should we do to avoid creating tightly coupled classes? We will cover 2 different approaches in this article but of course, there are other good approaches.
Decoupled Stage — Protocol Based Approach
We will create an abstraction and Swift Protocols are the perfect fit for that purpose. Now our ViewContoller does not have any idea about the implementation details of an API class. This is what we achieve and we successfully decoupled these into classes by creating Protocol.

A Abstracted API class via Protocol.

A ViewController that implements abstracted API class
Now we have flexibility with our implementation. We can test our API class using mocks, fakes, stubs, etc. whatever it needs. We can extend our API class with new functionalities without breaking our ViewController implementation. Small but effective change made.
Decoupled Stage — Adapter Based Approach
Imagine that we are dealing with 2 different Databases or let’s say data stores. One of them is remote running REST API service and another one is local cache storage. With our previous implementation, we are not able to switch smoothly between two data stores. But if we create a middle layer between ViewController and target data stores, in this case, our API class, we can switch one to another or we can implement both of them at the same time without breaking two separate implementations.

A Adapter class that connects any kind of data store with given ViewController.

A ViewContoller that uses Adapter implementation.
This way, our ViewController does not care about which data store it’s connected to and does not know about implementation details. Flexible and testable abstracted implementation. Now we have fully decoupled classes and indeed managed dependencies.
Conclusion
I gave a brief demonstration on how to decouple dependencies by making small but effective changes. There are two considerations to have when managing dependencies in your project, abstraction and protocol-oriented programming. You can also explore various implementations by doing more detailed analyses and research on these topics.



