Singleton Design Pattern in iOS

Omar Saibaa
6 min readOct 31, 2023

--

The Singleton design pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The Singleton pattern is one of the simplest and most widely used design patterns.

Example 1: 👶

Imagine you are in a classroom. There is only one teacher in the classroom, and all of the students need to talk to the teacher at some point.
The teacher is like a Singleton object. There is only one instance of the teacher, and all of the students need to use the same instance.

Example 2: 🍎

Imagine you have a box of cookies. You want to make sure that everyone in the house can eat a cookie, but you also don’t want anyone to eat all of the cookies at once.
You could use the Singleton pattern to create a cookie box object. The cookie box object would be responsible for keeping track of how many cookies are left in the box and for giving out cookies to people who want them.
Since there is only one instance of the cookie box object, everyone in the house will share the same box of cookies. This will ensure that everyone gets a cookie, but it will also prevent anyone from eating all of the cookies at once.

Example 3: 💻

A real technical example of the Singleton design pattern is the iOS app delegate. There is only one app delegate per app, and all code in the app has access to the app delegate.

How to implement the Singleton pattern in iOS?

  1. Create a class that you want to make a singleton.
  2. Add a private static property to the class to store the shared instance.
  3. Add a public static method to the class to get the shared instance.
  4. Make the constructor private to prevent other code from creating new instances of the class.

Example 4: 💪

Another example of the Singleton design pattern is the database connection object. The database connection object is a singleton object that is responsible for managing the connection to the database. There is only one database connection object per app, and all code in the app has access to the database connection object.

Let’s code it:

class DatabaseConnectionManager {
private static let sharedInstance = DatabaseConnectionManager()

private init() {
// Connect to the database.
}

public static func shared() -> DatabaseConnectionManager {
return sharedInstance
}

var connection: FMDatabase?

func connect() {
if connection == nil {
connection = FMDatabase(path: "database.sqlite")
connection?.open()
}
}

func disconnect() {
if connection != nil {
connection?.close()
connection = nil
}
}
}

To use the Singleton design pattern, you would simply call the shared() method to get the shared instance of the class. For example:

let databaseConnectionManager = DatabaseConnectionManager.shared()

// Connect to the database.
databaseConnectionManager.connect()

// Execute a SQL query.
let results = databaseConnectionManager.connection?.executeQuery("SELECT * FROM users")

// Disconnect from the database.
databaseConnectionManager.disconnect()

By using the Singleton pattern for the database connection object, you can ensure that there is only one instance of the object and that all code in your app has access to that instance. This can make your code simpler and easier to maintain.

Example 5: 🐉

The Instagram app allows users to share photos and videos with their followers. The app has a number of features, such as the ability to edit photos and videos, add filters, and write captions.

The Instagram app uses the Singleton design pattern to manage the user’s session information. There is only one instance of the session information object, and all code in the app has access to that object. This makes it easier for developers to write code that interacts with the user’s session information, and it also helps to reduce memory usage.

Let’s Code it:

class SessionManager {
private static let sharedInstance = SessionManager()

private init() {
// Fetch the user's session information from the API.
}

public static func shared() -> SessionManager {
return sharedInstance
}

var session: Session?

func getSession() -> Session? {
return session
}

func updateSession(session: Session) {
self.session = session
}
}

To get the user’s session information, developers would simply call the shared() method on the SessionManager class to get the shared instance of the class. Then, they could call the getSession() method on the shared instance to get the user's session information.

For example, the following code shows how to get the user’s access token:

let sessionManager = SessionManager.shared()

if let session = sessionManager.getSession() {
let accessToken = session.accessToken
}

By using the Singleton pattern for the session information object, the Instagram app can ensure that there is only one instance of the object and that all code in the app has access to that instance. This makes the code simpler and easier to maintain, and it also helps to reduce memory usage.

Here is an example of how to use the Singleton pattern in a view controller in the Instagram app:

class FeedViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let sessionManager = SessionManager.shared()

// Get the user's access token.
if let session = sessionManager.getSession() {
let accessToken = session.accessToken

// Fetch the user's feed from the API using the access token.
// ...
}
}
}

By using the Singleton pattern, the Instagram app can ensure that all code in the app has access to the user’s session information without having to pass the information around from class to class. This makes the code simpler and easier to maintain.

Advantages of the Singleton pattern in iOS:

  • Global access: The Singleton pattern provides global access to a shared instance of a class. This can be useful for accessing resources that need to be shared across the app, such as a database or a network connection.
  • Reduced memory usage: The Singleton pattern can help to reduce memory usage by ensuring that there is only one instance of a class. This is especially important for classes that consume a lot of memory.
  • Improved performance: The Singleton pattern can also improve performance by avoiding the overhead of creating multiple instances of the same class.

Disadvantages of the Singleton pattern in iOS:

  • Tight coupling: Overuse of the Singleton pattern can lead to tight coupling between classes, making it difficult to test and maintain the codebase.
  • Global state: Singleton introduces global state, which can lead to unintended side effects and make debugging complex.
  • Testability: Singletons can hinder unit testing, as objects depending on them are tightly coupled and cannot be easily mocked or stubbed.

Tips for using the Singleton pattern in iOS:

1- Use the Singleton pattern sparingly. Not all classes need to be singletons. Only use the Singleton pattern for classes that need to be shared across the app and that need to be globally accessible.

2- Make sure the Singleton class is thread-safe. This means that the Singleton class must be able to handle concurrent access from multiple threads. You can use a dispatch_once_t variable to ensure that the Singleton class is only initialized once, and you can use a mutex to protect access to the shared instance.
Let’s take an example:

import Foundation

class DatabaseManager {
private static let sharedInstance = DatabaseManager()
private static let dispatchOnce = dispatch_once_t()
private static var mutex = pthread_mutex_t()

private init() {
// Connect to the database.
}

public static func shared() -> DatabaseManager {
dispatch_once(&dispatchOnce, {
pthread_mutex_init(&mutex, nil)
})

pthread_mutex_lock(&mutex)
defer { pthread_mutex_unlock(&mutex) }

return sharedInstance
}

func fetchData() -> [User] {
// Fetch the user data from the database.
}
}

The dispatch_once variable ensures that the DatabaseManager class is only initialized once. The mutex variable is used to protect access to the shared instance of the DatabaseManager class.

To use the DatabaseManager class, you would simply call the shared() method. For example:

let databaseManager = DatabaseManager.shared()
let users = databaseManager.fetchData()

3- Provide a way to destroy the Singleton object when it is no longer needed. This is typically done by providing a destroy() method. This is useful for cases where you need to release resources that are being held by the Singleton object, such as a database connection or a network connection.

Finally:

The Singleton pattern is a design pattern that ensures that there is only one instance of a class. It is often used in iOS apps to manage shared resources, such as the user’s account information or the app’s database connection.

Thanks for reading and Don’t forget to claps, comment and follow me for more.

--

--