kelan.io

Singletons in Swift

I recently learned that Swift makes it really simple to make a singleton.

In Objective-C, we’re all used to the dispatch_once() pattern for singletons, and things like this.

@implementation C

+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    static id sSharedInstance = nil;
    dispatch_once(&onceToken, ^{
        sSharedInstance = [[self alloc] init];
    });
    return sSharedInstance;
}

// Rest of the class implementation

@end

But, in Swift, it’s even easier. Basically, if you do a static let property in your class (Swift’s documentation calls that a “type property”), then you can treat that like a singleton.

The Swift equivalent of the above Objective-C code is:

class C {
    static let sharedInstance = C()

    // Rest of the class implementation
}

// later, call it with
let c = C.sharedInstance

Much simpler!

Some Details

Another Example

Here’s a more realistic example, using a NSDateFormatter, from a Playground:

import Cocoa

class C {
    // You can even use a closure to build your singleton (and remember, it's done lazily)
    static let formatter: NSDateFormatter = {
        print("building date formatter")
        usleep(100)
        let f = NSDateFormatter()
        f.dateFormat = "MMMM YYYY"
        return f
        }()

    func formatDate(date: NSDate) -> String {
        return C.formatter.stringFromDate(date)
    }

    func doSomethingWithoutFormatter() {
        print("doSomethingWithoutFormatter()")
    }
}

let c = C()
// use the instance to see that the formatter hasn't been built yet
c.doSomethingWithoutFormatter()
// do a bunch of concurrent blocks that will all try to access the formatter
for _ in 0..<100 {
    dispatch_async(dispatch_get_global_queue(0, 0)) {
        print(c.formatDate(NSDate()))
    }
}

// Make the playground environment run GCD queues.
dispatch_main()

The output is:

doSomethingWithoutFormatter()
building date formatter
June 2015
June 2015
June 2015
...

You can see a few interesting things:

Other Tricks

You can make the type property use var, and then modify it later.

import Cocoa

class C {
    // You can even use a closure to build your singleton (and remember, it's done lazily!)
    static var formatter: NSDateFormatter = {
        let f = NSDateFormatter()
        f.dateFormat = "MMMM YYYY"
        return f
        }()

    func formatDate(date: NSDate) -> String {
        return C.formatter.stringFromDate(date)
    }
}

let c = C()
for _ in 0..<100 {
    dispatch_async(dispatch_get_global_queue(0, 0)) {
        print(c.formatDate(NSDate()))
    }
}

let newFormatter = NSDateFormatter()
newFormatter.dateFormat = "YYYY MMMM"
C.formatter = newFormatter

// Make the playground environment run GCD queues.
dispatch_main()

The output is:

2015 June
June 2015
June 2015
June 2015
June 2015
June 2015
June 2015
2015 June
2015 June
2015 June
2015 June
2015 June
2015 June
June 2015
2015 June
2015 June
2015 June
June 2015
2015 June
2015 June
June 2015
June 2015
2015 June
2015 June
2015 June
2015 June
June 2015
2015 June
June 2015
2015 June
June 2015
2015 June
June 2015
2015 June
2015 June
... (the rest are all the same "2015 June")

You can see the first few block executions raced to get the formatter before or after the change.

I don’t have a great example of when this would be useful, but I imagine it could be.

Also, because I haven’t been able to find any details of how this works internally, I’m not sure how thread-safe it is to actually set and get this value from different threads concurrently (as I do in the example above). Presumably it has some sort of atomic in the accessors, but I wouldn’t suggest using this in production code, without further experimentation or research.

Thanks

Thanks again to @jtbandes for help with this.

Update: 2015-07-28

I just saw this post (linked from This Week in Swift), where Hector Matos comes to the same one-line conclusion, and uses some breakpoints to prove that the dispatch_once() is actually being used. Nice.

Also, he reminds us to use a private init(), to prevent callers from creating instances without going through the singleton.

class C {
    static let sharedInstance = C()
    private init() {}  // <-- Force callers to use the singleton.

    // Rest of the class implementation
}