Waldo sessions now support scripting! – Learn more
App Development

Singletons in Swift: The Definitive Guide

Juan Reyes
Juan Reyes
Singletons in Swift: The Definitive Guide
August 9, 2022
7
min read

The purpose of this article is to provide a comprehensive and definitive guide to singletons in Swift. We'll explore the different aspects of the singleton pattern in the object-oriented programing paradigm and its unique implementation in Swift. So, if you're looking for guidance on singletons and how to use them, this article is for you.
First, we'll describe singletons and how they apply to Swift, then we'll show you some examples. Following that, we'll explain why singletons can be an excellent solution to a problem. Finally, we'll explore cases where they are not the best solution to a problem.

Now, if you don't have any experience in Swift or any background in programming, these subjects might be a bit much for you to grasp. So, we advise you to take your time and explore the Swift development guide and acquaint yourself with the language. You can find the official Swift development sources here.

With that out of the way, let's get into the theory.

A singleton is a programming pattern designed to provide a mechanism to create a globally accessible single-instance class.

What Is a Singleton in Swift?

A singleton is a programming pattern designed to provide a mechanism to create a globally accessible single-instance class.

In their official documentation on singletons, Apple states: "You use singletons to provide a globally accessible, shared instance of a class. You can create your own singletons as a way to provide a unified access point to a resource or service that's shared across an app, like an audio channel to play sound effects or a network manager to make HTTP requests."

In simple terms, this means is that Swift can only create one class instance of that singleton and that any other class in your project can access it, its properties, and its methods.

You might ask why that's useful. Well, there are some clear benefits from having a class possessing these properties, especially global accessibility. Setting variables on one part of the code and checking the values on another without needing to instance or pass parameters to methods is one of the most prominent cases.

Singletons are common in the many Apple platforms available for development. Some of the most well-known classes that use the singleton pattern in Swift and Objective-c are UIApplication, UserDefaults, and FileManager.

How to Create a Singleton in Swift

Alright, so let's demonstrate what a singleton looks like with some code.


class MySingleton { 
	static let shared = MySingleton() // The singleton holder 
	func sayHello() { 
		print("Hello from a singleton!")
}}
MySingleton.shared.sayHello() // Referencing the holder from the class itself and not an instance of it

And that's it.

As you can see, the initializer is set to private. This is important because it stops the code from trying to create a class instance of this singleton. Additionally, the static property called 'shared' in this class contains an instance of the class itself. This property is the window from which you can access the singleton class methods and properties from any other class.

If you're wondering why this works, it basically boils down to the fact that the property itself is static and self-contained inside the class. So, as long as you reference that property from the class itself and not an instance, you can access the sole instance that fulfills a singleton's requirements.

All that might sound a bit confusing, but it's really not. Let me demonstrate with an example.


	class MySingleton {
	   static let shared = MySingleton() // The singleton holder
	   private init() {}
	   var message: String?
	   func sayHello() {
	       print(message!)
	   }
	}
	class MyClass {
	   func addMessage(text: String) {
	       MySingleton.shared.message = text
	   }
	}
	class MyOtherClass {
	   func printMessage() {
	       MySingleton.shared.sayHello()
	   }
	}
	var myClass = MyClass()
	myClass.addMessage(text: "Hi again from this singleton")
	var myOtherClass = MyOtherClass()
	myOtherClass.printMessage()

As you can see, there are two additional classes in this example, 'MyClass' and 'MyOtherClass.' These classes both reference the singleton class in them. And while one is assigning a value to the message variable, the other uses it.

If you run this code, you'll see that the message is printed on the console correctly.

Now, why is this consistent with a singleton behavior? Because typically, a class should not be able to access or set values outside the scope of its parent container.

What this means is that if 'MySingleton' were not a singleton and instead were used as a regular class, the message variable used to print the 'printMessage' method of the 'MyOtherClass' would throw an error indicating that the message variable has not been set.

You can see this behavior if we change the code a little bit.


	class MySingleton {
	   static let shared = MySingleton() // The singleton holder
	//    private init() {}
	   var message: String?
	   func sayHello() {
	       print(message!)
	   }
	}
	class MyClass {
	   func addMessage(text: String) {
	//        MySingleton.shared.message = text
	       let singleton = MySingleton()
	       singleton.message = text
	   }
	}
	class MyOtherClass {
	   func printMessage() {
	//        MySingleton.shared.sayHello()
	       let singleton = MySingleton()
	       singleton.sayHello()
	   }
	}
	var myClass = MyClass()
	myClass.addMessage(text: "Hi again from this singleton")
	var myOtherClass = MyOtherClass()
	myOtherClass.printMessage()

This results in the following:

Swift singleton code preview


Yikes!

Benefits of Using Singletons in Swift

As discussed previously, there are a lot of benefits to the singleton pattern. One is the global access of methods and variables in this class.

Suppose you have a situation where you want a single point of access containing reference variables that don't mutate or you need to synchronize processes between threads in a project. In that case, singletons can be a great way to achieve that.

Now, you could say that global variables can do the same, but you can be absolutely sure of the number of instances available with singletons. In contrast, global variables do not offer you that benefit. Additionally, using singletons is more resource-friendly and less complex than creating object instances and passing them along.
One of the most common uses for singleton classes is for holding user data. This data usually needs to be conveniently accessible everywhere in the application, and its changes must be reflected everywhere.

The last case where a singleton would be the best approach would be for holding configuration settings. These settings must also be available everywhere, and changes must be reflected everywhere.

One of the most glaring issues with singletons is that it violates the principle of single responsibility.

Drawbacks of Using Singletons in Swift

One of the most glaring issues with singletons is that it violates the principle of single responsibility. This principle states that each class should have one responsibility or reason to change.

A singleton class commonly ends up taking on many responsibilities as you add more and more variables and methods that you need to globalize across your application. In addition, having numerous responsibilities and reasons to change is much more challenging to maintain as changes in one responsibility affects others.

Furthermore, singletons make unit testing too complicated to be practical in large and complex projects. That's because by introducing global states to the application, you cannot wholly isolate classes dependent on these singletons. And when you try to test these classes, you inevitably test the singleton as well. All this basically means that the code is now tightly coupled.

These are the common issues you might face when testing classes dependent on singletons:

  • The order in which you run tests now affects the outcome.
  • You can no longer run tests in parallel as you cannot guarantee the run order.
  • Values in the singleton the target class depends on will cause unpredictable behavior.

In Conclusion

In the world of development, and in mobile development in particular, having a mechanism to keep a state across an application seamlessly and effortlessly can be beneficial, particularly on small and straightforward projects where state dependency is low. However, as we have stated, there are a lot of drawbacks to overly relying on singletons. Chief among them is the loss of testing flexibility and reliability. That's not to say that you can't offset a lot of these drawbacks with cautious implementations. But in the end, singletons are just a tool, and it all comes down to your ability to foresee gotchas and avoid abusing the tool.

If you want to ensure that your code is rock solid and ready for prime time, we recommend that you check out Waldos' code-free testing solution.

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.