I don’t think anyone would disagree that the singleton pattern is the most common design pattern utilized in software development. Why is that so? Perhaps because it is conceptually the easiest to understand, or that it is actually applicable in many situations that arise again and again. Consider that a search for the phrase “singleton pattern” returns over three thousand results on StackOverflow. Contrast that with “memento pattern” which has a little over a hundred. Do you even know what a memento pattern is? We sure as hell don’t.
Developing OS X or iOS applications we frequently employ the singleton pattern as well. Many of our classes have names like ConfigurationManager
or FacebookController
. The terms manager and controller usually clue you in as to the nature of the singleton – there’s typically only one instance for your application. You probably don’t need more than one object instantiation to manage interacting with Facebook (or if you do, we’ve been doing it wrong all this time).
Examples of writing a singleton class in Objective-C abound. Here’s a quick refresher, edited for brevity. In our ConfigurationManager
header:
[objc]
@interface ConfigurationManager : NSObject {
}
+(id)sharedInstance;
[/objc]
And in our ConfigurationManager.m
:
[objc]
#import "ConfigurationManager.h"
@implementation ConfigurationManager
static ConfigurationManager* sharedInstance = nil;
+(ConfigurationManager*)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super alloc]init];
}
return sharedInstance;
}
-(id)init {
self = [super init];
return self;
}
@end
[/objc]
This code follows the classic singleton “algorithm” in that access to the single ConfigurationManager
is done through calling:
[objc]
ConfigurationManager* configurationManager = [ConfigurationManager sharedInstance];
[/objc]
Since sharedInstance
is an Objective-C class method you call it on the class (rather than an object). In return you get a pointer to an object that could only have been created and initialized once. That is guaranteed by the sharedInstance
method: if the single instance pointer is nil then allocate memory for the object and then initialize it. Otherwise, return the instance you have.
Of course, now we want to do this in Swift! If there’s anything that is certain, it’s that the actual logic of creating the singleton is going to pretty much be the same, it’s just a matter of getting the syntax right. Here we go!
In our ConfigurationManager.swift
file:
[objc]
import Foundation
class ConfigurationManager : NSObject {
class var sharedInstance:ConfigurationManager {
return ConfigurationManagerSharedInstance
}
init() {
super.init()
}
}
[/objc]
Let’s stop right there for a minute and explain. Everything looks “normal” thus far with the exception of the class var sharedInstance:ConfigurationManager
code. In Swift-speak this block of code is known as a read-only computed property. To decrease its esotericocity (pretty sure that isn’t a word but should be), consider this:
[objc]
class Circle {
var radius:Double = 0.0
init(radius r:Int) {
radius = r
}
var area:Double {
return M_PI * radius * radius
}
}
[/objc]
Can you spot the read-only computed property? It is of course, area
. That’s pretty logical: the area isn’t a function or a method of a circle. It’s a property of it. But the value of that property can change if you change the radius, so the property is computed and that computation involves the radius.
In our ConfigurationManager
example, we declared a class read-only computed property, that is, the property exists for the entire class, not for specific instances of the class. In Objective-C you’d call it a static instance variable.
Okay, back to the singleton pattern and its realization in Swift. We have a class property called sharedInstance
, and it is of type ConfigurationManager
. It returns something called ConfigurationManagerSharedInstance
but we haven’t defined what that is. Let
us. Nyuk, nyuk.
[objc]
let ConfigurationManagerSharedInstance = ConfigurationManager()
[/objc]
Note that this code is outside of the class definition but still in the ConfigurationManager.swift
file. What does it do? It declares, using the let
(think constant) keyword to define ConfigurationManagerSharedInstance
as an instance of the ConfigurationManager
class.
To use our new singleton, we merely have to call ConfigurationManager.sharedInstance
and due to what is known as Swift’s lazy initialization we know that ConfigurationManagerSharedInstance
won’t be initialized until someone accesses the sharedInstance
property. Try it for yourself if you don’t believe us. Create a simple iOS project with a file called Universe.swift
with something along the lines of this in it:
[objc]
class Universe {
class var sharedUniverse:Universe {
return SharedUniverse
}
init() {
NSLog("In the beginning God created the heavens and the earth.")
}
}
let SharedUniverse = Universe()
[/objc]
Then, in your application entry method (func application(...)
), try something like:
[objc]
NSLog("This is before the beginning…")
Universe.sharedUniverse
NSLog("This is after the beginning…")
Universe.sharedUniverse
[/objc]
You will only see the universe created once, as it should be.
So, there you have it. A convenient “guaranteed-to-run-only-once” singleton pattern you can use in Swift. The twist here was that we let
(we’re cracking ourselves up) the compiler and language do the work of warranting that the shared instance would indeed only be initialized once, and it would be initialized only after we accessed the sharedInstance
property for the first time.
It goes without saying that there are alternatives to this approach, and we didn’t come up with this approach to begin with. For background, alternative approaches, and the genesis of using the let
keyword accomplish this, see the StackOverflow post that started the hunt for using singletons in Swift.