Back to blog

Singletons In Swift Explained


Abhinav Girdhar
By Abhinav Girdhar | Last Updated on February 5th, 2024 1:21 pm | 5-min read

A singleton is a class of which exactly one instance exists, that can be globally accessed. How do you create a singleton in Swift? And why should or shouldn’t you?

In this app development tutorial, we’ll dive into singletons in Swift. You learn what the singleton design pattern is, and why it’s useful. We’ll discuss the syntax for creating singletons in Swift. And we’ll get into good and bad use cases for singletons.

What Is A Singleton?

A singleton is a class of which only one instance exists. A few examples:

  • A company has only one CEO
  • An API class has only one serial request queue
  • An operating system has only one file system
  • A solar system body revolves around one gravitational point
  • An app that does I/O has only one default FileManager
  • An airplane has only one flight deck

The second attribute of a singleton is that it has a global point of access. You can access a singleton, eg. call functions on it, from anywhere in your app’s code.

So, to summarize:

  1. A singleton is a class that has only one instance
  2. It can be accessed globally, i.e. anywhere in your code

In practical iOS development, you use singletons often. Typical classes like NotificationCenter, UserDefaults, SKPaymentQueue and FileManager have shared or default properties that are singletons.

At other times, you may wish to create a singleton yourself. A good use case is an API class that exposes singleton instance via its shared property. You use API.shared.makeAPICall() to access the API through a single, unified instance. This allows you, for instance, to manage API calls serially.

Before we discuss when singletons are best used (and when not), let’s find out how to code a singleton in Swift.

Coding A Singleton In Swift

This is the best way to create a singleton in Swift:

class API
{
static let shared = API()

private init()
{
// Set up API instance
}
}

And here’s how you use the singleton:

API.shared.doSomething()

We’re creating an API class that has one static property called shared. This property cannot be changed once set, because it’s a constant, and it’s declared statically.

That means we can access the shared property via the class API. This is often called a class property. Compare this to a normal instance property, which can only be accessed via an instance of a class.

What’s interesting is that the shared property initializes an instance of API within the API class. We’re sort of creating an API object that can be accessed through the API class. But there’s more…

The class initializer init() is marked with private. This private keyword ensures that the API class can only be initialized within the API class.

In other words, you cannot create an instance of API outside of the API class! This ensures that the API object we’ve created is the only instance in our code. After all, you can’t create more of it.

And now we’ve ensured the API class conforms to the two attributes of a singleton:

  1. Thanks to the static property shared, the API instance can be accessed globally
  2. Thanks to the private init(), the API class cannot be initialized outside of the API class

This all may sound a little abstract to you, so let’s expand the previous example with some more practical code. Here’s what:

class API
{
static let shared = API()
var isRequestPending = false

private init() { }

func makeAPIRequest()
{
if isRequestPending {
return
}

isRequestPending = true

// Make the API HTTPS request…
}

func onReturnAPIRequest()
{
isRequestPending = false

// Do something with request data…
}
}

The API class is mostly the same. It’s still a singleton, and it still uses those static let shared = API() and private init() bits of code.

Here’s what’s changed:

  • The API class now has an isRequestPending property. This is where the danger begins… See how the isRequestPending boolean ensures that only one API request can be done at a time? (Note that isRequestPending is an instance property.)
  • The API class also has a makeAPIRequest() function. Imagine that we can use this function to get some data back from a webservice API, like Twitter’s. In the function, you can see that a request can only be made when no other request is currently pending.
  • The API class also has an onReturnAPIRequest() function. This function is invoked when the API request returns, i.e. online data has been downloaded into the app. The isRequestPending boolean is set to false again, and the request data is processed.

And here’s how we can use the API singleton anywhere in our code:

API.shared.makeAPIRequest()

There’s something else we need to discuss. The API class now manages something called state. You can see “state” as a feeling: you’re either happy or you’re sad, or you’re angry, and so on. You can switch from one state to the other.

The API class can switch between two states:

  • A state in which isRequestPending is false
  • A state in which isRequestPending is true

As you’ll learn in the next section, state and singletons can wreak all sorts of havoc on your code. Managing state poorly is the single biggest reason for singleton misuse.

When To Use Singletons

When do you use singletons? The book Design Patterns: Elements Of Reusable Object-Oriented Software by the Gang of Four has the following to say. Use the singleton pattern when:

  • there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point
  • when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code

That’s complex, but what it boils down to is:

  • Use a singleton when your code requires no more than one instance of a class (i.e., the CEO in the company)
  • And when it must be accessible from anywhere in your code (i.e., the file system)

Another use case is subclassing. A global variable in your code can’t be easily subclassed, so that’s why you use a singleton class. Additionally, singletons can be unit tested by using dependency injection. You replace the API instance by an APIMock instance, and gain the ability to unit test API calls without making the actual network requests.

And when do you not use singletons? To answer that question, we’ll have to go back to the state principle we discussed earlier.

A common pitfall for beginner iOS developers is to manage state and its dependencies poorly. Imagine you’re building an app that uses the API class we worked with earlier.

Every time you expand the API class, you tack on more and more properties, such as:

  • A userID property that keeps track of the logged in user, once the login() API call has been made
  • A tweets property with Twitter data, once the getTweets() call has been made
  • A spinner property with a UIActivityIndicatorView that you add to a view controller when a request has started

At first, this makes a lot of sense to do. After all, the API class can be accessed anywhere in your code. So, in the Tweet View Controller you can use the API.shared.tweets array, and in the Settings Controller you can use userID to quickly tell the API whose settings to change.

Unfortunately, your state is now all over the place. The API class has dependencies to a bunch of classes that aren’t related to the single responsibility of the API class. Your code has become a bowl of spaghetti, all tangled up. The code may work OK, but it’s impossible to maintain and extend.

Let’s look at an example. The onReturnAPIRequest() function we defined earlier is on the brink of becoming tightly coupled…

Here’s what we’re considering:

  • The onReturnAPIRequest() is called when an API webservice request returns, i.e. when data comes into the app. This data needs to go somewhere – a Tweet View Controller for example. How do you pass the data from the API to the view controller?
  • An obvious choice is to just create a reference to the viewController in the API class. When the data comes in, you can code something like viewController.tweets = tweetsData. This is poor architecture, unfortunately, because now the API and the view controller are tightly coupled. It’s impossible (or hard) to unit test, and likely to create problems when extending either class.
  • It’s better to choose a mechanism that doesn’t tightly couple both classes. One option would be to pass a closure to onReturnAPIRequest(), which is executed when the request returns. This closure can then contain code to handle the incoming data. Another option would be to use NotificationCenter to pass the data to the view controller, or to use a Database class to handle the data.

The singleton design pattern has gained some controversy, simply because it’s easy to misuse. When using singletons, be mindful of state and dependencies. Just because it’s easy to have global access to state, it doesn’t mean it’s a good idea.

Further Reading

And that’s all there is to it! Getting to know singletons is a worthwhile objective, especially if you’re interested in app architecture and systems design.


Abhinav Girdhar

Founder and CEO of Appy Pie

App Builder

Most Popular Posts