dispatch_once in Swift : a 24 hours journey

Jerome
Jerome
Jul 23, 2015 · 4 min read

I love GCD, and dispatch_once is a terrific API: You have the guarantee that your code is gonna be executed once & once only, and on top of that with thread-safety.

I’m learning Swift and the other day, I had to deal with NSNumberFormatter. NSFormatter are great but expensive to instantiate, so you should reuse them as much as possible.

So when I started typing NSNumberFormatter, my Obj-C developer mind told me to use dispatch_once.

Image for post
Image for post

I’m greeted with 2 errors:
“Cannot assign to immutable value of type ‘dispatch_once_t’” -> Easy, I’ll just use a var.
“Static properties may only be declared on a type” -> This one is annoying, because I need my variable to be static. Otherwise, what’s the point?

After a few minutes on Google, I ended up coding the ugly-but-working-struct-inside-a-method

func formatSomeStuff() {
struct Static {
static var onceToken: dispatch_once_t = 0
static var numberFormatter: NSNumberFormatter? = nil
}
dispatch_once(&Static.onceToken) {
Static.numberFormatter = NSNumberFormatter()
if let numberFormatter = Static.numberFormatter {
numberFormatter.numberStyle = NSNumberFormatterStyle.PercentStyle
numberFormatter.maximumFractionDigits = 1
}
}
//Do stuff
}

It works but let’s face it, it’s ugly and I had the feeling I was going against Swift.

This “Static properties may only be declared on a type” really annoyed me and I decided to file a radar about this. I was pretty sure it would be closed as duplicate but I filed it anyway. (You will see later why this is important).

Later that day, I attended a Brooklyn Swift meetup http://www.meetup.com/Brooklyn-Swift-Developers/ and one of the slide was about lazy-loading. This looked like a great fit for what I was trying to do, so here we go! (In the meantime, I had move my NSNumberFomatter code into his own method so I can simply use numberFormatter() wherever I needed it).

class MyClass {
lazy var numberFormatter: NSNumberFormatter = {
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.PercentStyle
numberFormatter.maximumFractionDigits = 1
return numberFormatter
}()
func formatStuff() {
//Do stuf with (self.)numberFormatter
}
}

Much Much Better: it starts to finally feel like Swift! Note that I considered having a global var with the same lazy-loaded closure, but as a rule of thumb I avoid putting stuff in the global space (outside of MyClass I don’t need to access my numberFormatter, so the global space is not the right spot for this).

I was still not perfectly happy about it, this numberFormatter is not a property of MyClass, I need it, but it’s not exactly a variable. I chose to define it as a variable only because I wanted to use the lazy-loading.

The next morning I received an email from a Swift Developer at Apple who read my Radar.

Two main things from this email:
- . This is the main lesson I learned. Swift provides new ways to do things: you’re not supposed to simply “translate” your app, but you need to learn new things & new awesomeness.
- : So… what if instead of a lazy var I used a static let inside my class? It should work!

class MyClass {
static let numberFormatter: NSNumberFormatter = {
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.PercentStyle
numberFormatter.maximumFractionDigits = 1
return numberFormatter
}()
func formatStuff() {
//Do stuf with (self.)numberFormatter
}
}

And Voila! It’s not marked as a var anymore and the intent is clear: It’s a static constant. We benefit under-the-hood from all the greatness of GCD and our code is super readable.

Turns out the dispatch_once & lazy loading relationship was available all along on Apple’s Blog.

Don’t stop your research at the first Stack Overflow answer, try to find a better way to do it (and comment back on that Stack Overflow if you do find a better way to do it).

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store