The Singleton Pattern – Adopting Design Patterns in Swift

The use of the singleton pattern is a fairly controversial subject among certain corners of the development community. One of the main reasons for this is that the singleton pattern is probably the most overused and misused pattern. Another reason this pattern is controversial is that it introduces a global state into an application, which provides the ability to change the object at any point within the application. The singleton pattern can also introduce hidden dependencies and tight compiling. My personal opinion is that, if the singleton pattern is used correctly, there is nothing wrong with using it. However, we do need to be careful not to misuse it.

The singleton pattern restricts the instantiation of a class to a single instance for the lifetime of an application. This pattern is very effective when we need exactly one object to coordinate actions within the application. An example of good use of a singleton is if the application communicates with a remote device over Bluetooth and we also want to maintain that connection throughout the application. Some would say that we could pass the instance of the connection class from one page to the next, which is essentially what a singleton is. In my opinion, the singleton pattern, in this instance, is a much cleaner solution, because with the singleton pattern any page that needs the connection can get it without forcing every page to maintain the instance. This also allows us to maintain the connection without having to reconnect each time we go to another page.

Singleton design pattern

Understanding the problem

The problem that the singleton pattern is designed to address is when we need one and only one instance of a type for the lifetime of the application. The singleton pattern is usually used when we need centralized management of an internal or external resource and a single global point of access. Another popular use of the singleton pattern is when we want to consolidate a set of related activities needed throughout the application that does not maintain a state in one place.

A true singleton is implemented using a reference (class) type.

When using the singleton pattern, one of the biggest concerns is multi-threaded applications with race conditions. The issue occurs when one thread changes the state of the singleton while another thread is accessing it, producing unexpected results. As an example, if the TextValidation classes stored the text to be validated and then we called a method to do the validation, one thread could change the stored text before the original thread did the validation. Before implementing this pattern, it is advisable to understand how a singleton will be used in your application.

Understanding the solution

There are several ways to implement the singleton pattern in Swift. In the method that we use here, a single instance of the class is created the first time the class constant is accessed. We will then use the class constant to gain access to this instance throughout the lifetime of the application. We will also create a private initializer that will prevent external code from creating additional instances of the class.

Note that we use the word class in this description and not type. The reason for this is that the singleton pattern can only truly be implemented using a reference type.

Implementing the Singleton Pattern

Let’s look at how we implement the singleton pattern with Swift. The following code example shows how to create a singleton class:

class MySingleton {
    static let sharedInstance = MySingleton() 
    var number = 0
    private init() {}
}

We can see that, within the MySingleton class, we created a static constant named sharedInstance, which contains an instance of the MySingleton class. A static constant can be called without having to instantiate the class. Since we declared the sharedInstance constant static, only one instance will exist throughout the lifecycle of the application, thereby creating the singleton pattern.

We also created the private initiator, which cannot be accessed outside of the class, which will restrict other code from creating additional instances of the MySingleton class.

Now, let’s see how this pattern works. The MySingleton pattern has another property, named number, which is an integer. We will monitor how this property changes as we use the sharedInstance property to create multiple variables of the MySingleton type, as shown in the following code:

var singleA = MySingleton.sharedInstance 
var singleB = MySingleton.sharedInstance 
var singleC = MySingleton.sharedInstance
singleB.number = 2
print(singleA.number)
print(singleB.number)
print(singleC.number)
singleC.number = 3
print(singleA.number)
print(singleB.number)
print(singleC.number)

In this example, we used the sharedInstance property to create three variables of the MySingleton type. We initially set the number property of the second MySingleton variable (singleB) to the number 2. When we printed out the value of the number property for the singleAsingleB, and singleC instances, we saw that the number property for all three equaled 2.

We then changed the value of the number property of the third MySingleton instance (singleC) to the number 3. When we printed out the value of the number property again, we saw that all three now have a value of 3. Therefore, when we change the value of the number property in any of the instances, the values of all three change because each variable is pointed to the same instance.

In this example, we implemented the singleton pattern using a reference (class) type because we wanted to ensure that only one instance of the type existed throughout the application. If we implemented this pattern with a value type, such as a structure or an enumeration, we would run the risk of there being multiple instances of the type.

If you recall, each time we pass an instance of a value type, we are actually passing a copy of that instance, which means that, if we implemented the singleton pattern with a value type, each time we called the sharedInstance property we would receive a new copy, which would effectively break the singleton pattern.

The singleton pattern can be very useful when we need to maintain the state of an object throughout the application; however, be careful not to overuse it. The singleton pattern should not be used unless there is a specific requirement (requirement is the keyword here) for having one, and only one, an instance of the class throughout the lifecycle of the application. If we are using the singleton pattern simply for convenience, then we are probably misusing it.

Learn more about Swift Programming

Where To Go From Here

Visit Apple Developers article on Singleton Design Pattern in Swift XCode – Singleton Pattern in Swift

NOTE

Keep in mind that, while Apple generally recommends that we prefer value types to reference types, there are still plenty of examples, such as the singleton pattern, where we need to use reference types. When we continually tell ourselves to prefer value types to reference types, it can be very easy to forget that there are times when a reference type is needed. Don’t forget to use reference types with this pattern.

Written by

XR Developer responsible for end-to-end development of XR solutions spanning multiple domains, by using various XR and WebXR libraries.

Leave a Reply