The singleton pattern is a widely used design pattern in software development. Despite its popularity, it's often considered an anti-pattern. Why is that? In this episode, I explain what the singleton pattern entails and how to create singletons in Swift.

What Is a Singleton

Singletons are easy to understand. The singleton pattern guarantees that only one instance of a class is instantiated. That's simple. Right?

The singleton pattern guarantees that only one instance of a class is instantiated.

If you've worked with Apple's frameworks, then chances are that you've already used the singleton pattern. Take a look at these examples. They probably look familiar.

// Shared URL Session
let sharedURLSession = URLSession.shared

// Default File Manager
let defaultFileManager = FileManager.default

// Standard User Defaults
let standardUserDefaults = UserDefaults.standard

// Default Payment Queue
let defaultPaymentQueue = SKPaymentQueue.default()

The singleton pattern is a very useful pattern. There are times that you want to make sure only one instance of a class is instantiated and that your application only uses that instance. That's the primary and only goal of the singleton pattern.

The default payment queue of the StoreKit framework is a fine example. An application should never create an instance of the SKPaymentQueue class. The operating system uses the StoreKit framework to create a payment queue, which your application can use. The default payment queue is accessible through the default() class method of the SKPaymentQueue class. This is a good example of how the singleton pattern should be applied.

Global Access

But the singleton pattern has a side effect that's often the true reason for adopting the singleton pattern, global access. But having global access to the singleton object is no more than a side effect of the singleton pattern.

Unfortunately, many developers use the singleton pattern to have easy access to the singleton object from anywhere in their project. The default payment queue is accessible through the default() class method. This means that any object in a project can access the default payment queue. While this is convenient, that convenience comes at a price.

If you want to learn more about the problems surrounding the singleton pattern, then I recommend reading Are Singletons Bad. In that article, I discuss this topic in more detail.

How to Create a Singleton In Swift

In this episode, I list two recipes for implementing the singleton pattern in Swift. The first implementation shouldn't be used, though. It merely illustrates a few concepts of the Swift language.

Global Variables

The most straightforward technique to create a singleton is by defining a global variable.

let sharedNetworkManager = NetworkManager(baseURL: API.baseURL)

class NetworkManager {

    // MARK: - Properties

    let baseURL: URL

    // Initialization

    init(baseURL: URL) {
        self.baseURL = baseURL
    }

}

By defining a variable in the global namespace of the project, any object in the module has access to the singleton object. We could, for example, access the singleton object in the application(_:didFinishLaunchingWithOptions:) method of the AppDelegate class.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    print(sharedNetworkManager)
    return true
}

In Swift, global variables are initialized lazily. This means that the initializer is run the first time the global variable is referenced.

An added benefit of Swift's approach is that the initialization is performed using the dispatch_once function. It guarantees that the initializer is invoked only once. That's important since you only want to initialize the singleton object once.

Using a global variable has several shortcomings. The most important problem is cluttering the global namespace. Another downside is that the initializer of the NetworkManager class cannot be declared private. This means that multiple instances of the class can be instantiated. Let me show you a much better, and my preferred, implementation in Swift.

Static Property and Private Initializer

A few years ago, Swift introduced static properties and access control to the language. This opened up an alternative approach to implementing the singleton pattern in Swift. It's much cleaner and elegant than using a global variable. Take a look at the updated example.

class NetworkManager {

    // MARK: - Properties

    static let shared = NetworkManager(baseURL: API.baseURL)

    // MARK: -

    let baseURL: URL

    // Initialization

    private init(baseURL: URL) {
        self.baseURL = baseURL
    }

}

Accessing the singleton is intuitive and it clearly conveys that we're dealing with a singleton.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    print(NetworkManager.shared)
    return true
}

Several implementation details have changed. First, the initializer is private. It means that only the NetworkManager class can create instances of itself. That's a significant advantage.

Second, we declared the shared static constant property. This property gives other objects access to the singleton object of the NetworkManager class.

It isn't necessary to mark static properties with the lazy keyword. Remember what I said earlier, the initializer of global variables and static properties are executed lazily by default. That's another benefit.

I want to share one more implementation of the singleton pattern. This implementation is a bit more complex. The main difference is that the singleton object is instantiated in a closure, allowing for a more complex initialization and configuration of the singleton object.

class NetworkManager {

    // MARK: - Properties

    private static var sharedNetworkManager: NetworkManager = {
        let networkManager = NetworkManager(baseURL: API.baseURL)

        // Configuration
        // ...

        return networkManager
    }()

    // MARK: -

    let baseURL: URL

    // Initialization

    private init(baseURL: URL) {
        self.baseURL = baseURL
    }

    // MARK: - Accessors

    class func shared() -> NetworkManager {
        return sharedNetworkManager
    }

}

The static property is declared private. The singleton object is accessible through the shared() class method.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    print(NetworkManager.shared())
    return true
}

Cocoa and Singletons

With these implementations in mind, we have mimicked the interface many Cocoa frameworks have adopted in Swift.

// Shared URL Session
let sharedURLSession = URLSession.shared

// Default File Manager
let defaultFileManager = FileManager.default

// Standard User Defaults
let standardUserDefaults = UserDefaults.standard

// Default Payment Queue
let defaultPaymentQueue = SKPaymentQueue.default()

Are Singletons Bad

In Are Singletons Bad, I explain in detail what type of problems the singleton pattern can introduce in a project. My advice is to use singletons sparingly. Very sparingly. If you're about to create a singleton, take a moment and step back. Is there another option you may be overlooking? Is it absolutely necessary to use the singleton pattern?

Even though there's nothing inherently wrong with singletons, most developers use it for the wrong reasons, that is, convenience. They disguise global variables as singletons.

Dependency Injection

Even if you decide to use singletons in a project, that doesn't mean you have to access them from anywhere in your project. You can still use dependency injection to pass the singleton object to the objects that need it.

By adopting dependency injection to pass singletons around, the interface of your class remains clear and transparent. In other words, the interface of the class describes its dependencies. This is very, very useful. It immediately shows which objects the class needs to perform its duties.

One of the things I miss about Objective-C is the header file. The header file summarizes the public interface of a class, including the dependencies of the class. This is sometimes less obvious in Swift.