This is the third post and screencast in a series I’m doing on Swift development.
Source code examples are available on GitHub
Singletons are a popular design pattern in programming. If you are new to Swift, you may be wondering how you can create singletons in Swift. In Objective-C, you might have tried Grand Central Dispatch‘s dispatch_once to create a singleton. Well, the other day, I came across a situation in Swift that I thought would be a good example of using a singleton.
I needed to use the NSNumberFormatter class to change an integer like “15” into a currency like “$15.00” for a table view I was displaying. Because initializing an NSNumberFormatter can be an expensive operation, I didn’t want the performance of the app to be affected by this, especially if I was going to be using the same NSNumberFormatter configuration throughout the app. So, I decided to use a singleton.
Now, there’s a great stack overflow post all about the different ways to create a Singleton in Swift, however, it doesn’t provide an in-depth practical example and since NSNumberFormatter is a class that gets used a lot, I thought I would share an example of using these patterns with this class. Ok, let’s get started.
First, we’re going to import the Foundation library.
import Foundation
Then, we’re going to declare a class called CurrencyFormatter, which is going to inherit from the NSNumberFormatter class.
class CurrencyFormatter: NSNumberFormatter {
In order to inherit from the NSNumberFormatter class, you have to implement a required init function. All we do is just call the same function with super, and then the pass the aDecoder parameter to the function. This isn’t important to our problem but it’s required, so you can mostly ignore it.
required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
Override Init()
Now, we are going to override the standard “init” function, so we can set some default values on initialization. Inside the init function, first we need to call the “super.init” function. Next is code to make the currency formatting work correctly.
override init() { super.init() self.locale = NSLocale.currentLocale() self.maximumFractionDigits = 2 self.minimumFractionDigits = 2 self.alwaysShowsDecimalSeparator = true self.numberStyle = .CurrencyStyle }
Type Property Constants – version 1.2 or above
If you’re using Swift 1.2 or above, we’re almost done. All we need to do is declare a type constant property. To declare a type constant property on a class, we use the “static let” keywords and call it sharedInstance and then we’ll initialize it to an instance of our CurrencyFormatter class.
static let sharedInstance = CurrencyFormatter()
Now our singleton should be ready. Here’s the final code.
import Foundation class CurrencyFormatter: NSNumberFormatter { required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override init() { super.init() self.locale = NSLocale.currentLocale() self.maximumFractionDigits = 2 self.minimumFractionDigits = 2 self.alwaysShowsDecimalSeparator = true self.numberStyle = .CurrencyStyle } // Swift 1.2 or above static let sharedInstance = CurrencyFormatter() }
NSNumberFormatter has a method called “#stringFromNumber” and all we need to do is call “#stringFromNumber” on the sharedInstance singleton and pass in the number. So, we’ll just pass in 20 and then we’ll unwrap the optional so we can see it.
println(CurrencyFormatter.sharedInstance.stringFromNumber(20)!) //$20.00
You can see now that our number is formatted to $20.00 and if we copy this and change it to 50, you can see it works as well.
println(CurrencyFormatter.sharedInstance.stringFromNumber(20)!) //$20.00 println(CurrencyFormatter.sharedInstance.stringFromNumber(50)!) //$50.00
Before Swift 1.2, Class Variable Properties and Nested Structs
If you’re using a version of Swift below 1.2, you can’t actually declare type constants on reference types like classes, so we’ll have to do this a different way. Go ahead and remove the type constant we created.
This time, we’re going to declare a class variable property, which we’ll call our sharedInstance. But instead of initializing it, we’re going to declare it as a CurrencyFormatter type and then we’re going to declare this class variable as a read-only computed property.
class var sharedInstance: CurrencyFormatter { }
A computed property means instead of storing a value for this property, it just computes it every time that it’s called. In this case, it means we have to return an instance of CurrencyFormatter every time that this property is called. However, we can make it return the same instance by creating a singleton.
First, we’re going to declare a struct inside of this property. We declare a struct called Static.
class var sharedInstance: CurrencyFormatter { struct Static { } }
Structs in Swift are value types and in versions below 1.2, value types can have type constants which will allow us to create a Singleton of our CurrencyFormatter class. We’re going to declare it a type constant using static let, and then call it instance and then initialize it to an instance of our currency formatter class.
class var sharedInstance: CurrencyFormatter { struct Static { static let instance = CurrencyFormatter() } }
Then we need to return our Static.instance property (and singleton) at the end of the computed property.
class var sharedInstance: CurrencyFormatter { struct Static { static let instance = CurrencyFormatter() } return Static.instance }
Here’s the final code using the class variable and nested struct.
import Foundation class CurrencyFormatter: NSNumberFormatter { required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override init() { super.init() self.locale = NSLocale.currentLocale() self.maximumFractionDigits = 2 self.minimumFractionDigits = 2 self.alwaysShowsDecimalSeparator = true self.numberStyle = .CurrencyStyle } class var sharedInstance: CurrencyFormatter { struct Static { static let instance = CurrencyFormatter() } return Static.instance } }
And now our numbers are formatted again, so we’re getting the correct currency formatting.
println(CurrencyFormatter.sharedInstance.stringFromNumber(20)!) //$20.00 println(CurrencyFormatter.sharedInstance.stringFromNumber(50)!) //$50.00