Singleton Design Queries
In this part, we go through different questions and issues people have with using Singletons. If you haven’t you can read about the Singleton in more detail from part 1 and part 2
Q:When should we use Singleton?
1. there should only ever be one, and to make sure of it, make it impossible to create one other than the one (or at the very least make it close to impossible or difficult, or severely discouraged).
2. commonly you’d create a protocol including all of the functions that the clients will ever need to call from the singleton, and everywhere it is used, have the client have a property of that protocol type which is injected into it, rather than by calling the singleton itself.
You can have the default implementation of that property be the singleton. That way you can create a mock of it for testing.
Q: is it easy to test singleton? or another class that uses singleton?
- If you’re testing the Singleton class implementation, you don’t have to conform to a protocol. You can invoke its methods directly and assert the received output matches the expected one.
- If you’re testing another class that uses the Singleton instance directly, it can be hard to test. It is recommend you don’t use the Singleton instance directly. Instead, you can inject the instance via Constructor Injection. Then, you can decide to inject a protocol or a subclass — both facilitate testing with Singletons since you can use a test-double such as a Stub or a Spy to test the collaboration. So during tests, you won’t need to use a real singleton instance.
Q: Potential Problems with Singleton if used like NetworkClient.shared all across the codebase?
- High-coupling between modules (in this case between the UI and the API)
- Hard to extend the functionality without changing existing code (a violation of the Open/Closed principle).
- Hard to test as the NetworkClient is an implicit dependency and it’s cumbersome to replace the shared instance.
- If the shared instance is a mutable `var` then you need to be concerned about thread-safety and temporal coupling (this also prevents running tests in parallel reliably).
Q: What is the potential issue with using singleton references from external frameworks throughout an application?
Even though this approach provides convenience for developers to easily access the singletons anywhere in the code base, however, if the singleton reference is used throughout the app, it can create a tight coupling between the client and the external framework.
Q: How can you break the tight coupling between your application and an external framework that uses singletons?
A simple way to break the tight coupling on external frameworks is to use dependency inversion (instead of accessing the concrete singleton instance directly). By hiding the third-party dependency behind an interface that you own and that you can extend (e.g., protocol/closure), you can keep the modules of your app agnostic about the implementation details of another external system. Such separation can protect the codebase from breaking changes when updating the external framework, make your code more testable, and also allow you to replace it with another framework in the future easily.
Q5: is Singleton's thread-safe?
Accessing the static let for the first time is thread-safe in Swift cause let is immutable and lazily loaded so the first thread that accesses it will instantiate it and no other threads can mutate it.
But mutating the Singleton state isn’t as any thread at any point in time can read and write so we need to cater for it.
Q6: Normally when we create Network client it is always a singleton so when exactly are multiple Network clients necessary
There are many reasons to have different implementations of an NetworkClient. For example, you can create test-double implementations for testing purposes. You can also change between frameworks such as Moya, Alamofire, and URLSession without breaking other modules. You may also want to provide an implementation with hardcoded behavior for demoing purposes. And so on!
Q7 What are the pros and cons of using a single instance of HTTPClient for all loaders and has different instances of HTTPClient for each loader
the pros are using a single instance of `HTTPClient` for all loaders are mostly about optimization. You can easily share resources such as the in-memory/disk URLCache and runtime URLSession configuration, such as `httpMaximumConnectionsPerHost`, global headers, and authentication delegates. The cons are that you probably would have to make it thread-safe, which can complicate the implementation.
On the other side, you’d use different instances of `HTTPClient` for each loader when you want independent caching and/or configurations such as global headers and authentication for a different service.
If you share the same `HTTPClient` or not, that’s a composition detail the loaders don’t need to know about. The `HTTPClient` doesn’t even need to implement the Singleton pattern. You can simply create a single instance of the `HTTPClient`
Reference Links:
https://iosacademy.essentialdeveloper.com/p/ios-lead-essentials/