An iOS Engineer’s Perspective on Singleton Anti Pattern

In software development, a common design pattern is the singleton pattern, which is used to ensure that only one instance of a class can be created. However, while the singleton pattern can be useful in certain situations, it can also lead to problems if overused or implemented incorrectly. In this article, we’ll explore the singleton anti-pattern and its potential pitfalls, as well as provide some solutions to help mitigate these issues.
Problem
The singleton anti-pattern occurs when a developer uses a singleton for a class that was not intended to be used in this way. In Swift, a singleton is typically implemented using a static variable or a static function that returns a single instance of the class. Here’s an example:
class DataManager {
static let shared = DataManager()
private init() { }
func fetchData() {
// code to fetch data goes here
}
}In this example, the DataManager class is designed to fetch data, and we’ve created a singleton instance using a static variable called shared. This is a common pattern used for objects like network managers or data controllers. However, there are some cases where using a singleton in this way can cause problems.
For example, imagine that you have a User class that represents a user in your application. You might be tempted to use a singleton to store information about the currently logged-in user. Here’s an example of what that might look like:
class User {
static let shared = User()
private init() { }
var username: String?
var email: String?
var isLoggedIn: Bool = false
}The problem with this approach is that it can make testing and debugging difficult. If you have multiple tests that rely on the User class, and each test modifies the User.sharedinstance, it can be challenging to ensure that your tests are independent and not interfering with one another. Additionally, if you need to add new functionality to the User class, you may find that it’s difficult to modify the singleton instance without breaking existing code that relies on it.
Solution
The best way to avoid the singleton anti-pattern is to carefully consider whether a singleton is truly necessary for the class in question. In some cases, it may be better to use dependency injection to pass instances of a class to other objects that need them, rather than relying on a singleton.
In the case of the User class, it may be better to use dependency injection to pass the user object to the objects that need it. Here’s an example of what that might look like:
class User {
var username: String?
var email: String?
var isLoggedIn: Bool = false
init(username: String?, email: String?, isLoggedIn: Bool) {
self.username = username
self.email = email
self.isLoggedIn = isLoggedIn
}
}
class SomeOtherClass {
let user: User
init(user: User) {
self.user = user
}
}In this example, we’ve removed the singleton instance of the User class and added an initializer that takes the necessary parameters. We can now create multiple instances of the User class and pass them to the objects that need them using dependency injection. This makes testing and debugging much easier since each object can have its own instance of the User class.
Conclusion
The singleton anti-pattern can be a pitfall for developers if used incorrectly or overused. It’s important to carefully consider whether a singleton is truly necessary for a particular class, and to consider other options like dependency injection. By avoiding the singleton anti-pattern, you can write code that is easier to test
Please 👏🏻 if you like this post. It will motivate me to continue creating high-quality content like this one.
Support Me
Thank you for taking the time to read my blog post! If you found it valuable, I would greatly appreciate it if you could share the post on Twitter and LinkedIn, etc. Your support in spreading the word about my content means a lot to me. Thank you again!
Follow me
I hope you found this post helpful. If you want to stay up-to-date with my latest work, be sure to follow me on my page or my Twitter.






