The claim of divinity: Singleton and Swift
In this article, you are going to take a deep dive into the Singleton pattern. I wrote this article after reading two books(Design patterns: elements of reusable object-oriented software and Head First Design Pattern) couple of Youtube videos and many other articles(Links are available at the end of the article). This article is written with having Swift language in mind. This post is intended to be used as a complete reference. Good read.
Table of contents
- Description
- Why do we need Singleton
- Why shouldn’t we use Singleton
- Implementation (in Swift)
- Complementary notes on Singleton implementation
- Swift singletons
- The correct way to share resources
- Singleton VS Class VS Static
- Thread-safe Singleton
- Singleton use cases
Description
Normally when we define a class our intention is to create a model of attributes and methods that we can then create instances from to be able to use them in different places. We define different attributes and we specify getter and setter for each one or inject the values from the initializer of the class. So then we can have multiple objects from that specific class.
Singleton pattern on the other hand helps you to make it possible to only have one instance of a class and then the instance would be available globally (Only and only one instance). This means a Singleton class can only have one instance and no more and that instance is shared between all objects within the app.
Let’s take a look at descriptions that both books provided for us:
Headfirst design pattern on page 169:
The Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.
Gang of four:
The singleton pattern is a way to make sure that a class only has one instance and it provides a single point of access to it. The pattern specifies that the class itself should be responsible for keeping track of its sole instance.
So Singleton is basically a class that provides two conditions:
- provide global access to the instance
- makes sure there is only one instance
Why do we need Singleton?
There are times that we want to have only one object of a class and that single object is used throughout our app and the existence of more than one instance of that class would create problems and break our app. This can be an Instance of the Database or Having only one Logged in user at each moment.
Real-world analogy:
The government is an excellent example of the Singleton pattern. A country can have only one official government. Regardless of the personal identities of the individuals who form governments, the title, “The Government of X”, is a global point of access that identifies the group of people in charge.
Why shouldn’t we use Singleton?
As we read there are two characteristics to being a Singleton:
Having global Access and only one instance.
These two are ironically both the strength of Singletons and Achilles heel of them.
Singletons provide a globally mutable shared state
By architecture, in programming, we try to avoid global instances because when something is accessible globally that means you haven’t the power to control that instance and it is exposed to all mechanisms in your apps and makes it impossible for you to track what is happening to the instance. And changes can happen to the instance without you knowing it. (It makes unit testing a cumbersome task.) This global access can act against us and we know that access to objects should be limited to the part of our system that really needs it. The global accessibility of Singletons makes shared resources accessible from anywhere, especially from code that should not have any access. Even value types like Swift structures and enumerations can access a singleton, which is a bad practice. When any part of an app can access or change the global state, you get weird and hard to fix bugs.
Whenever I think about Signletons there is a famous quote that pops into my mind
I suppose it is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail. Abraham Maslow
Because Singletons are easy and convenient to use, they may grow on our app and if we don’t pay attention closely to how and when to use them we may use them everywhere and for anything, from resources to the database. And as developers we tend to do things in the easiest form possible, so we should keep in mind that sometimes convenience is the exact opposite of future rest. But in those cases, we are just taking advantage of Singleton(in a bad way) and using them just as global variables. This is an alarming sign that you should prevent.
Sacrificing Transparency for Convenience
When we rely on Singletons to manage a particular part of our app, we may use other frameworks or libraries(The code that other developers have written) and we meet our coding needs through the Singleton interface, in these cases, we are blindly relying on a Singleton that we are not aware of its underlying code, is it really worth it? it’s a question that you should ask yourself and a trade-off that you should accept if you choose to use Singletons.
Losing Track of Everything
Now we know that if we use Singletons we are exposing its instance to our app. This opens a window for all our app processes and mechanisms to be able to affect the instance and change it. When we have a global static accessible instance all systems(different parts of our app) can access it and can change it even without us knowing about it. This will result in nasty errors that tracking them is somewhat impossible and demanding.
Scalability
The whole idea of forcing an application to have a Singleton class is absurd and can create a barrier to the scalability of your app unless you are 1000% sure that this is what you really need.
It’s completely fine to only have a single object but it’s not, to force your app to only have one. It’s not a good idea to make it impossible to create a second object.
Testing is difficult
It may be difficult to unit test the client code of the Singleton because many test frameworks rely on inheritance when producing mock objects. Since the constructor of the singleton class is private and overriding static methods is impossible in most languages, you will need to think of a creative way to mock the singleton. Or just don’t write the tests. Or don’t use the Singleton pattern.
Assumption of having only a single instance
The assumption that there will always be only one instance of a singleton is often broken. There are times that we assume we ultimately need only one instance of a class and then create that class as a Singleton but after that our app grows we may need to have another object from that class, but we are in a state where our app relies on the assumption that we are only having one instance of that class. Now there isn’t much that we can do about it, we may even need to rewrite a large part of our code. This change leads to significant refactoring of all code that relies on a singleton. So always have in mind that in the future, we might need to have different instances of that object to handle different locations in the file system or different storage solutions. Core Data, for example, is usually used with only one managed context, but also supports multiple ones.
Massive Singleton
Singleton classes often become bulky classes. Since it’s easy to access a singleton from anywhere, the chances are high that code that needs to be shared ends inside an existing Singleton. Massive view controllers are not the only monolithic objects you should avoid in iOS. The same happens to Singletons.
Unclear separation
Singletons encourage code with unclear separation. One of the fundamental principles of good software development is the separation of concerns. Singletons make access to shared resources a widespread concern across many objects. When a concern is spread too wide, it’s hard to keep track of it when specifications change and code needs to be updated. This leads to inconsistencies in your codebase that can lead to severe problems like data loss or corruption.
Existence for the lifetime of the application
Singletons carry the state around for the lifetime of the application. There are cases in which you need to reset the shared state. When you can have multiple instances, you can discard the old one and then create a new one. In a singleton, instead, resetting state might not be so natural and might require specific and complex code.
Some other concerns with Singletons
Violates the Single Responsibility Principle.
The Singleton pattern can mask bad design, for instance, when the components of the program know too much about each other.
The pattern requires special treatment in a multithreaded environment so that multiple threads won’t create a singleton object several times.
Implementation (in Swift)
There could be different implementations of a Singleton class according to the book “Gang of four”, there should be a getter for returning the static instance
Now you have written a Singleton but as you see this implementation is kinda goofy and we can improve upon it by removing the “private” keyword before instance and then we don’t need to have “getInstance()”.
class Singleton{static let instance: Singleton = Singleton()private init(){}var value = 10}//This is the way you can access tha class attributeslet result = Singleton.instance.value
of course, there is more to it than this mere implementation that we are going to discuss further along the way.
Note: statics in swift are lazily loaded, so this initialization won’t be executed until the first time that you try to access it.
The “static” keyword ensures global access.
If you are the most certain that you won’t need to extend the singleton class in the future you can even add the “final” keyword before the “class” keyword. It ensures that this class won’t be inherited and has some positive performance effects if you labeled your classes with a “final” keyboard. But you should be aware that the final classes can’t be involved in inheritance.
final class Singleton{static let instance: Singleton = Singleton()private init(){}var value = 10}//This is the way you can access tha class attributeslet result = Singleton.instance.value
If you want to practice safe coding you can make the instance a computed property and then check if there is only one instance of it.
final class Singleton{static let instance: Singleton = {if Singleton.instance != nil {return Singleton.instance}else{let instance = Singleton()return instance}}()private init(){}
So Singleton implementation is basically what you’ve seen so far, any other variation that you may see only should comply with two principles to be called a Singleton class:
1. Private initializer
2. One global instance
Complementary notes on Singleton implementation
There are some arguments that I like to mention that may have a solid stance and may inspire you to think more whenever you are trying to implement a singleton.
Inheritance
According to Gang of four, we shouldn’t make a singleton class private because we need to be able to extend the class by inheritance and add functionality to it.
On the other hand, we should be aware that in swift we can make extensions without any subclassing. Of course, we won’t be able to override the class behaviors then, so it’s a trade-off!
Note: If you want to involve your Singleton in inheritance “final” keyword should be omitted from the class signature.
Shared instances (singleton with lower case)
If you have experience with Swift singleton you may have noticed that unlike what we have read so far Swift singletons or better say “Shared instances” doesn’t have this private initializer. They do have a global shared static instance but they don’t have a private initializer and you can actually create a new instance from them. An example for these shared instances is “URLSession” that we usually use it through its shared instance like given below
URLSession.shared.dataTask(with: URL)
but if you try to create a new instance of URLSession you can see that it’s possible and you just need to provide a URLConfiguration and pass it to the initializer
let newURLSessionInstance = URLSession(configuration: <#T##URLSessionConfiguration#>)
there are many other examples here and there with the same exact implementation. In the Swift community, you may find developers who all call these singletons but you should be aware that these are just shared instances and they are not real-book-definition Singletons.
Note: If terminology is something that you pay attention to (you should) you can implement Singleton with “instance” object and shared classes with “shared object”.
myShaerdClass.shared
//Shared objectmySingletonClass.instance
//Singleton object
Swift singletons
There is no denying that singletons have been popular for a long time in iOS development. There are many reasons for that.
First of all, Apple does not show many Swift design patterns in its documentation, but singletons are right there in the list.
So it looks like Apple encourages the use of singletons. Moreover, Apple’s documentation shows how to create a singleton, but does not discuss the implications. That makes it look like the singleton pattern has no drawbacks.
The second reason is that Apple uses singletons extensively in the iOS SDK. Here are a few examples of the most common ones:
UIApplication.shared
UserDefaults.standard
FileManager.default
URLSession.shared
OperationQueue.main
UIApplication is one of those examples that even apple follows the original definition of Singletons and that makes sense in this case because we shouldn't be allowed to have more than one current running app yes?
Note: UIApplication class comes from Objective-C, a realm where there is no concept of private and public methods. So, the UIApplication initializer is public, and the compiler allows you to use it anywhere in your code. But at runtime, it will throw an exception and crash your app.
The other classes in the list above are not real Singletons there are shared instances. These shared instances exist only for the convenience of a globally accessible instance. And that’s the biggest reason why singletons are popular. They are very convenient.
The correct way to share resources
What makes Singletons wrong as shared resources interfaces is their global accessibility and not the essence of sharing resources. So how should we share resources? The answer is easy but implementation is somewhat a pain. We should use dependency injection. With dependency injection resources are shared to only those objects that are in need of resources, not all our code. This practice makes our code safer and avoids future headaches that may cause because of using Singleton only for sharing resources.
Dependency injection is a mechanism where an object receives its dependencies from another, external object. Contrast it with the singleton pattern, where an object retrieves its dependencies.
Dependency injection is the de-facto technique to share resources between objects without using singletons. More generally, it makes the dependencies of an object replaceable. This is useful in many contexts, including unit testing with test doubles.
Dependency injection is simple to implement. All you need is public property for each dependency you want to inject into an object.
Singleton VS static
You may have noticed by now that instead of using Singletons we can use static variables and functions in a class and they will be again accessible globally the same way as a singleton is. And even using static variables and functions is more convenient.
//Accessing the Static variable
StaticHelper.state
//Accessing the singleton
SingletonHelper.instance.state
Differences between them
- The first difference is that you can have multiple instances of the static class
2. The second difference is that when you want to access a static attribute within the class because there isn’t any object instantiated you should use it as it is mentioned above.
ClassName.staticAttributeName
3. Not a difference, more a tip you don’t want to use static members on resource-intensive objects like API calls or Database.
Other than that they are the same.
Note that you don’t have a Singleton struct because structs are value types.
Thread-safe Singleton
Stop reading if you don’t know what is a thread, go and search then come back continue your journey through this piece of the article.
In Swift, any variable declared with the “let” keyword is a constant and, therefore, read-only and thread-safe. When a variable is declared as “var” it becomes mutable and not thread-safe unless the data type is specifically designed to be thread-safe when mutable. So swift collection types such as Array and Dictionary are not thread-safe when they’re mutable. Now you may think ok why do I need to care? Normally your arrays and variables are not global and even if you share them with other classes it’s not prone to cause any trouble right? so shouldn’t be the same for Singletons? of course not!
Singletons are globally accessible therefore the variable within a singleton class can be changed if they are marked as “var” and because your instance is static, if anything changes a variable value, the value will change for your whole app. There are cases that you rely important process of your app on the value of a Singleton class object and in those cases, you may want to sometimes change the value and update it. The problem is, your app may try to change the value from two different places at the same time, in this case, you will face some nasty errors.
The can be read by many threads simultaneously, it’s unsafe to allow one thread to modify the object while another thread is reading it. The singleton in the above example doesn’t prevent this issue from happening and the solution is mentioned below.
What is dispatch queue? According to apple
An object that manages the execution of tasks serially or concurrently on your app’s main thread or on a background thread.
Thread safety is accomplished by having a computed property “mutableAttribute” which uses an internalQueue
to access the real “privateMutableAttribute” property.
Also, in order to have better performance internalQueue
is created as concurrent. And it means that it is needed to add the barrier
flag when writing to the property.
Note: Theoretically speaking we should specify our queue as a Serial queue but we can achieve the same behavior for a better performance by using a concurrent queue with a barrier flag. A serial queue would not support multiple reads at the same time. Since we don’t need to block access to just one read at a time, we use a concurrent queue to allow multiple reads. We do however need to support just one write a time, so we use the barrier flag
The “barrier” flag basically ensures the process will be executed when all previously scheduled processes on the queue are finished.
Concurrent VS Serial Dispatch Queues
Concurrent Dispatch Queues
A concurrent queue executes multiple tasks at once, where the execution of them starts according to the order they’re added to the queue, but the execution of each task can be finished in a different order since those tasks can be executed in parallel. The dispatch queue manages the threads on which tasks are executed.
Serial Dispatch Queues
A serial dispatch queue executes only one task at a time. In most cases, serial queues are often used to synchronize access to a specific value or resource to prevent occurring data races.
As parallel and concurrent reads and writes produce data corruption, consequently causing the app to crash, making sure that both reads and writes do not execute in parallel, could be a solution. In other words, preventing both reads and writes occurring at the same time seems to solve the issue.
For that, “serial queues” are used as they guarantee the execution of tasks one at a time.
Singleton use cases
Usecase 1: The UserManager
With every project I run, there is always the need to store some user data (this doesn’t mean it gets synced to a server). This Singleton contains information about the user and the device, information I might need to access from different locations inside the app.
Usecase 2: The KeyManager
This is my favorite manager by far. In this manager, I persist and store any key I need to run the app. Normal keys are stored in the UserDefaults (plain text storage). Secret keys are stored in the Apple Keychain.
Usecase 3: The DataManager
I use this to persist data I synchronize or get from a data API. This manager is called from the Splash Screen to load the data once. For example, a data manager could maintain app information (color schema, font sizes, special banners). This Data Manager would have the following structure:
Usecase 4: Preview management
As I use SwiftUI there are many cases that I want to hot load my view and to do so I need some kind of dummy data to show on the view(only in the development stage and then I remove it). To meet this need I normally create a Singleton class and then populate my dummy data within it. And then I simply write use extension to ease the access to the singleton in PreviewProviders
extension PreviewProvider {static var dev: MySingletonClass{return MySingletonClass.instance}}
Usecase 5: Using FileManager
There are times that you may want to use the file manager to save data that you have downloaded in this case I create a generic file manager class to be able to save data in different folders. An example is given below: