Mastering Singleton
in Swift is key in iOS development interviews. In this tutorial, we’ll break down common interview questions about Singletons, covering everything from implementation details to real-world use cases. Get ready to confidently handle Singleton-related questions and level up your iOS interview game!
Q.1 What ia a Singleton in Swift?
Ans. The Singleton
in Swift is a creational design pattern
that ensure a class has only one instance and provides a global point of access to that instance.
Q.2 How we can implement Singleton
in Swift?
Ans. We have to take care of two things while implementation of Singleton
–
private
initializer to prevent external instantiation. This is crucial for enforcing that only one instance of the class is created.private
, instantiation can only happen within the classprivate init() { }
static
property to provide a global point of access to the single instance of the class.static let shared = YourSingletonClass()
static
variables are lazy
initiated so it’s a lazy initialization.
Example
class SomeSingletonClass { // static property to provide a global point of access static let shared = SomeSingletonClass() private init() { // Private constructor to prevent multiple instances } func someFunction() { // Perform additional requirments } }
Breakdown-
class SomeSingletonClass
: This defines a class named SomeSingletonClass
.
static let shared = SomeSingletonClass(): This line creates a static constant
property named shared
of type SomeSingletonClass
. The static
keyword ensures that this property is associated with the class itself rather than an instance of the class. It will provide a global point of access to the single instance of the class SomeSingletonClass
private init() { }: This private
initializer ensures that no external code can create instances of the SomeSingletonClass
class. The Singleton
pattern dictates that there should be only one instance of the class, and this private
initializer enforces that by preventing external instantiation.
With this implementation, you can access the shared
instance of the SomeSingletonClass
class using SomeSingletonClass.shared
throughout your code, and you can be confident that there will only ever be one instance of the SomeSingletonClass
class in your application.
Note– In swift static
variables are lazy
initiated so it’s a lazy initialization.
Q.3 Give some examples of Apple SDKs using Singleton pattern?
Ans- Below are some Apple frameworks
that used Singleton
pattern-
UIApplication.shared
UserDefaults.standard
FileManager.default
URLSession.shared
OperationQueue.main
Q.4 Is singleton thread safe in Swift?
Ans. In Swift, the singleton
pattern itself doesn’t inherently provide thread safety.
Whether a singleton is thread-safe or not depends on how it’s implemented.
A singleton
is a design pattern that ensures a class has only one instance and provides a global point of access to it. If you’re designing a singleton
in Swift and you want to make it thread-safe, you need to consider potential race conditions
that can occur when multiple threads try to access or modify the singleton
instance simultaneously.
Q.5 How to make singleton thread safe in Swift?
Ans. We will try to understant thread-safety in Singleton
with an example. Below is a simple example of a Singleton
class which is not thread safe –
class SomeSingletonClass { // static property to provide a global point of access static let shared = SomeSingletonClass() private var dict = [String:Any]() private init() { // Private constructor to prevent multiple instances } func setValue(key: String, value: Any) { // Perform additional requirments dict[key] = value print("key: \(key) , value: \(value)") } }
above implementation of a singleton class SomeSingletonClass
looks correct for the most part. It follows the typical singleton
pattern with a private
initializer to prevent the creation of multiple instances and a static
property to provide a global point of access. However, there’s a potential issue related to thread safety in setValue
method.
The setValue
method modifies the dict
property, and if this method can be called concurrently by multiple threads, you might encounter race conditions.
In Swift, collections like Array, Dictionary are not thread safe.
Now, If we try to use our singleton class SomeSingletonClass
like below –
func testThreadSafeSingleton() { for i in 1...200 { SomeSingletonClass.shared.setValue(key: "\(i)", value: i) } }
and call it like this
testThreadSafeSingleton()
Here testThreadSafeSingleton
function is a simple test case where we’re calling the setValue
method of singleton SomeSingletonClass
in a loop. In this case we will not face any issue and we can get our expected output like below –
key: 1 , value: 1
key: 2 , value: 2
key: 3 , value: 3
key: 4 , value: 4
.
.
.
key: 198 ,value: 198
key: 199 , value: 199
key: 200 , value: 200
Now, if we modify our test function testThreadSafeSingleton()
to run concurrently using Swift’s DispatchQueue:
func testThreadSafeSingleton() { let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent) for i in 1...200 { concurrentQueue.async { SomeSingletonClass.shared.setValue(key: "\(i)", value: i) } } }
We will get unexpected behaviour or app will get crash.
How to make Singleton Thread Safe :
There can be multiple approches to make singleton
thread-safe. We will use DispatchQueue with barrier to resolve this problem. We will re-write our singleton
class
class SomeSingletonClass { // static property to provide a global point of access static let shared = SomeSingletonClass() private let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue1", attributes: .concurrent) private var dict = [String:Any]() private init() { // Private constructor to prevent multiple instances } func setValue(key: String, value: Any) { // Perform additional requirments concurrentQueue.async(flags: .barrier) { [weak self] in self?.dict[key] = value print("key: \(key) , value: \(value)") } } }
now if we call it
testThreadSafeSingleton()
We will get expected output.
key: 1 , value: 1
key: 2 , value: 2
key: 3 , value: 3
key: 4 , value: 4
.
.
.
key: 198 , value: 198
key: 199 , value: 199
key: 200 , value: 200
Breakdown
We have updated implementation of SomeSingletonClass
and introduced a concurrent dispatch queue with the .barrier flag to ensure thread safety when modifying the dict
property.
The use of a barrier ensures that the block is the only one executing on the concurrent queue at a given time, preventing concurrent writes and ensuring data consistency.
To summarize the changes:
- We have added a concurrent dispatch queue (concurrentQueue) to our singleton class.
- The
setValue
method now usesconcurrentQueue.async(flags: .barrier)
to perform the modification of thedict
property in a thread-safe manner. - The
[weak self
] capture list is used to prevent strong reference cycles.
This implementation is an example of how to use a concurrent queue with a barrier to achieve thread safety. It’s a common and effective approach in Swift for handling concurrent data access.
Q.6 What is the use/advantages of singleton in swift?
Ans. The singleton pattern in Swift has its advantages and use cases. Here are some of the advantages of using the singleton pattern:
- Single Instance: The primary purpose of a singleton is to ensure that a class has only one instance and to provide a global point of access to it. This can be useful when exactly one object is needed to coordinate actions across the system.
- Global Access: The singleton pattern provides a centralized point of access to an instance, making it easy to access its functionality from anywhere in the code. This can simplify code and reduce the need to pass instances around.
- Lazy Initialization: The singleton pattern allows for lazy initialization of the instance. The instance is created only when it’s first needed, improving resource usage and startup time.
- Shared Resources: If a single resource, such as a configuration manager, logging service, or database connection, needs to be shared across multiple parts of the application, a singleton can be a convenient way to manage that resource.
Q.7 When Should we use Singleton?
Ans. If there is scenario that we need exactly one instance of class, and creation of second instance can be dangerous then we should use singleton
.
Example–
AppDelegate – There should be only one instance of an application and hence it is the right candidate for singleton
Q.8 What are the disadvantages of singleton in swift?
Ans. The Singleton pattern
, like any design pattern, is a tool that can be used appropriately in certain situations and can be misused in others.
Whether the Singleton pattern is considered “bad” depends on the context and how it’s applied.
Below are some cons and considerations while using singleton –
- Global State – if multiple classes are updating the state of a
singleton
object in Swift, it may become challenging for a programmer to trace and identify which specific class or part of the code modified the state at a given point in time. This lack of clarity can lead to difficulties in understanding, debugging, and maintaining the codebase, as the programmer may lose insight into the origin of state changes.This scenario is particularly relevant when using a singleton for shared state management, where various parts of the application might independently update the state without explicit tracking of the source of the updates. - Dependency Ripples: Challenges with Centralizing Functionality in a Singleton
Let suppose you are using a common code inside singleton, a small change within the singleton’s functions requires modifications across multiple locations where it’s utilized.
This highlights the challenges associated with maintaining and evolving code that heavily relies on a central singleton for common functionality. - Data Inconsistency: Global accessibility of a
Singleton
can lead to data inconsistency if not properly managed, especially in a multithreaded environment. Inconsistent data can lead to unexpected behavior and bugs - Hidden Dependencies: The use of Singletons can hide dependencies, making it less clear which components depend on the Singleton. This can result in code that is harder to reason about, test, and maintain.