Advanced iOS Series — Episode I: How to deal with Singletons in modular system design?

Enes Buğra Yenidünya
5 min readAug 8, 2022

At some point in our iOS Development journey, some of us are struggling with going further, feeling a bit stuck between learning new topics or developing existing skills. I recently wanted to start a new article series about Advanced iOS Development that will help developers facing this situation.

So, this is the first article in Advanced iOS Series. We will look at the management of Singleton objects in modular system design. There will be four steps in this system design. We will plan better system design in every step.

By the way, you can find the source code of this article’s project here.

Table Of Contents

  1. Classical Singleton Object Instance Management
  2. Extended Singleton Object
  3. Protocol Oriented Management
  4. Adapter Design Pattern Applied

Classical Singleton Object Instance Management

We are often using singletons all around our project. Is this good? This is a separate topic for discussion. We will not talk about this today. In fact, we will be focusing management of singleton object usage.

Almost all of us have seen a singleton class coded and used in the following structure.

Classical way of creating Singleton Class
Classical way of using Singleton Instance
Classical way of using Singleton Instance
Classical way of using Singleton Instance

It seems easy to use like this but there are a couple of problems here. First, there are singletons everywhere and it’s hard to understand the changes that are being made all around the project. What would happen if we change a property in ApiClientStepOne view singleton instance or change the arguments of a function? It would affect other parts of the project even if it’s not relevant. Second, it’s not testable. We are not able to inject mock instances of ApiClientStepOne class. Third, it’s not modular. If we try to move ApiClientStepOne class to a separate module there would be 3 different referenced classes to this new module.

Can we make our implementation better? Of course it’s possible.

Extended Singleton Object

Let’s say we moved our ApiClientStepTwo class to another module. Now, how can we make our classes adapt to this change? We have to change our code because it’s tightly coupled with our ApiClientStepTwo class.

Logic is no longer in ApiClientStepTwo module.
Extended singleton class in a different module.
Extended singleton class in a different module.
Extended singleton class in a different module.

What do we get with these improvements? If you move a class to another module (SPM or any kind of Framework), now you separated the dependencies and you can easily extend your module by need. It’s sounds good right? Well, let’s say better for now.

What if we need to implement a bunch of new functionalities? We would repeat the same code as many times as we need it and that’s not good behavior for coding large-scale applications.

Our 3 classes are still dependent on the ApiClientStepTwo module. That means if you want to add another module that is implemented by the ApiClientStepTwo module, you might have wanted to change the other 3 classes.

Protocol Oriented Management

Now we reversed the dependencies and our 3 classes have their own protocols for communication with ApiClientStepThree module. Our API module has a generic execute function for service calls. We are not depended to the ApiClientStepThree module directly.

ApiClientStepThree module now has a generic execute function.
Protocol oriented based HomeStepThree class.
Protocol oriented based RegisterStepThree class.
Protocol oriented based LikeStepThree class.

It doesn’t look bad right? Most of the projects are stuck at step 2. So far so good. Now we got a testable and less dependent modular system design. We are dealing with singleton in-class initializers. It is a dependency management way, injecting dependencies into initializers. We are going to talk about Dependency Management in another episode of the series.

Adapter Design Pattern Applied

Beyond the good. Adapter design pattern is used for breaking the dependencies between our client and another module, in this case ApiClientStepFour class.

We created new Adapter classes and put them between our base classes and ApiClientStepFour class. Now, our client doesn’t know about ApiClientStepFour class’s capabilities and there is not any dependency between them.

We can move our module wherever we want and it’s now going to create any problem for us.

As you can guess we will discuss and talk about Adapter or any other useful Design Patterns.

Final version of ApiClientStepFour module.
New Adapter class between ApiClientStepFour and HomeStepFour
Final version of HomeStepFour. Adapter class is used.

I coded new adapter class only for Home module. It would be code repetition for others.

Conclusion

We have made step by step improvements to achieve modular system design in our singleton class. We got closer to the our goal every step. Now we have more testable, manageable and modular system.

Please, do not forget to review the source code of the project and follow me. Advanced iOS Series will continue to progress with new topics.

--

--

Enes Buğra Yenidünya

iOS Engineer — Freelancer #iOS #swift #mobileappdevelopment #software #apple