Singleton Pattern in Swift

Korey Hinton Blog

Singleton Pattern Defined

According to Wikipedia: "In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object". If you've programmed in Objective-C before then you already know it interprets the "restricts" part of that definition loosely. There's no real way to make an object only be allowed to be a Singleton instance in Objective-C, and as far as I know the same holds true for Swift. It is your responsibility to know or to document that it is meant to be used as a singleton.

A singleton is a global object and that one single instance can be referenced from any file. This pattern should only be used when it makes sense that there can only be 1 of that object. One example is the UIApplication singleton. There is only 1 application so this makes sense. Good use cases for the singleton pattern are usually when you need to manage something in particular throughout your app. I like to break out some of the complexity in the AppDelegate into its own singleton, like for the Core Data work I like to have a DataAccess singleton. I have found this pattern to be especially useful with a class that persists data or wrapping a framework that persists or syncs data from various places in the app. When you have multiple places in the app that need to update a central storage location then it could be a potential candidate. While singleton objects are generally frowned upon due to their global complexity they are also solving complexity issues when used correctly. Even Apple uses them for notifications, user defaults, and file management.

It is really this simple

Here is the simplest solution in Swift.

class DataAccess {
}
let sharedDataAccess = DataAccess()

So how would you access it? Simple:

sharedDataAccess

Test it!

This works because Swift's let is lazy-initialized and is thread safe.

class DataAccess {
    var number : Int!
}

/* Singleton */
let sharedDataAccess = DataAccess() // this will be lazy-loaded when first called


class ClassOne {
    init() {
        sharedDataAccess.number = 1
    }
}

class ClassTwo {
    init() {
        // sharedDataAcess.number equals 1 here as expected
        sharedDataAccess.number = 2
    }
}

ClassOne()
ClassTwo()
// sharedDataAccess.number equals 2 here as expected

But, isn't this just a global variable?

Yes it is. If you want it to be accessed from the class you could add a class function or class var that returns the global variable. In this case you might want to make it private.

class DataAccess {
     class func sharedDataAccess() -> DataAccess {
        return _sharedDataAccess
    }
}
private let _sharedDataAccess = DataAccess()

It would be accessed like this:

DataAccess.sharedDataAccess()

Or if you prefer a variable:

class DataAccess {
     class var sharedDataAccess : DataAccess {
        return _sharedDataAccess
    }
}
private let _sharedDataAccess = DataAccess()

And accessed by:

DataAccess.sharedDataAccess

Should I let it be global or encapsulate it in the class?

While I try to avoid making anything global I think the alternative creates too much overhead. You'd need to create an unnecessary method to just return a variable and then anytime you have to access the singleton you'll need to type the class as a prefix and that is also unnecessary overhead. You could argue that keeping it encapsulated within the class makes it easier to use since you can autocomplete and the class it belongs to seems like a natural way to access it. So far the Apple's APIs I've seen encapsulates it but that might just be because they were written in Objective-C.

DataAccess.sharedDataAccess.number = 1

as opposed to:

sharedDataAccess.number = 1

Where it could really get bad:

DataAccess.sharedDataAccess.myObject.myValue = 1

compared to quite not so bad:

sharedDataAccess.myObject.myValue = 1

A big argument for not having a global variable is namespace issues where you might run into variable name clashing in other files. For a single iOS app I don't ever see this as too big of a problem. As a Swift developer I know that global string constants and global functions can be accessed in any other file of the app. Singletons are probably so few that I should already know that there's a sharedDataAccess variable used throughout the app.

Wait what if I want something more elegant and complicated.

Go for it. But, please don't ask me to maintain it j/k

dispatch_once

In case you really feel its not right unless you see a dispatch_once in it:

class DataAccess {
    class var sharedDataAccess : DataAccess {
        struct Static {
            static var onceToken : dispatch_once_t = 0
            static var instance : DataAccess? = nil
        }
        dispatch_once(&Static.onceToken) {
            Static.instance = DataAccess()
        }
        return Static.instance!
    }
}

This is a slightly better alternative, but I'd still prefer a global let variable

class var sharedInstance : SharedData {
    struct Singleton {
        static let instance = SharedData()
    }
    return Singleton.instance
}

Singleton with custom initializer

Lets create a singleton that uses a custom initializer. You probably won't do this too often with singletons, since they are maintaining their own state throughout the use of the app. Luckily the constructor is only called in one place. Let's say we have a server that we use for various iOS apps. Each iOS app has their own unique access key so they only download content for their particular app. If we wanted to require the access key we can:

class ResourceDownloader {
    var accessKey : String
    init(accessKey withAccessKey: String) {
        accessKey = withAccessKey
    }
}
let sharedDownloader = ResourceDownloader(accessKey: "MY-RESOURCE-KEY-2014-FOR-APP-X")

A complete example

If you are new to singletons it could help to see an example. Let's assume we have to do a lot of file access code and have decided to create our own file manager singleton to wrap the calls to NSFileManager. For simplicity's sake let's say the user has buttons with full file paths that have an associated IBAction callback for the view controller and the table view controller has cells with the full file paths in the cell's text label. To show how state can be updated we will track how many file copies it has performed and make sure we don't go over the max copies allowed which in this case is only 1. Since the properties of the singleton object stay in memory from the time it is lazy-loaded all through the app's lifecycles, re-launching the app would allow for another file copy to take place.

FileManager.swift

import Foundation

class FileManager {

    let documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString

    let maxCopies = 1
    var copies : Int = 0

    private func handleError(error: NSError) {
        println(error)
    }

    func copyToDocuments(path: String) -> Bool {
        var toPath = documentsDirectoryPath.stringByAppendingPathComponent(path.lastPathComponent)
        var error : NSError? = nil

        var copied = NSFileManager().copyItemAtPath(path, toPath: toPath, error: &error)

        if error != nil {
            handleError(error!)
        }
        return copied
    }
}
let sharedFileManager = FileManager()

ViewController.swift

class ViewController : UIViewController {
    @IBAction func pressedFileButton(sender : UIButton) {
        if sharedFileManager.copies < sharedFileManager.maxCopies {
            if sharedFileManager.copyToDocuments(sender.titleLabel!.text!) {
                sender.enabled = false
                sharedFileManager.copies += 1
            }
        }
    }
}

TableViewController.swift

class TableViewController : UITableViewController {
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        var cell = tableView.cellForRowAtIndexPath(indexPath)
        if sharedFileManager.copies < sharedFileManager.maxCopies {
            if sharedFileManager.copyToDocuments(cell!.textLabel!.text!) {
                cell!.userInteractionEnabled = false
                sharedFileManager.copies += 1
            }
        }
    }
}

While the 2 different view controller's are accessing the sharedFileManager object independently they are still referencing the same exact object and incrementing the number of copies will be reflected if accessed in the other view controller.

Using Singletons to wrap frameworks

In the example above we saw how singletons can be used to wrap framework/third-party code. Now that you know the singleton pattern beware of going crazy and trying to use it where it doesn't belong. Not all third-party code has to be wrapped. Its important to look at the benefits and see why you are using a singleton. In the FileManager case we get nicer API calls without having to deal with NSError or looking up the documents directory. If this was all we were doing we could easily do an extension or helper function without needing to break out an entire class and singleton pattern. There are always other alternatives to singleton patterns. Use it when it feels right. Are you handling a ton of notifications in the app delegate? Are you writing the same code over and over again to call framework code? Could it be encapsulated? And would a singleton wrapper be preferred? One example where I felt like a singleton class worked well is with In App Purchases. Store Kit's API can be kind of intense and it sends notifications on various transaction states as well as download states. By having a singleton object I could filter those notifications and send out my own. Also when the app was done handling the transactions a simple call to my singleton made it easy to finish the transaction or update it on its state from various places in the app.

Alternatives to Singletons

Singletons make it easy to share resources to a variety of objects. Here are some similar patterns:

Monostate Pattern

The monostate pattern is similar to the singleton pattern. I've never used it myself but it looks to be interesting enough to explore more. It seems to be syntactic sugar around a singleton. You are able to initialize a new instance that is the same as the single instance (or maybe its the same state). So its like a transparent singleton. While its usage seems easier, it is also probably less readable and seems more like a clever trick.

Context Object

Context objects are objects that store information and can get passed to or inherited by various objects. In the case of sharing resources throughout the app like the case of a singleton you could have an appContext object. I've seen this done in other platforms but for iOS development we have an AppDelegate singleton object and Apple chose the singleton design pattern instead. Also, inheritance is not ideal either. In Objective-C NSObject inherits a lot of traits that every object has to have. In Swift you don't have to inherit from NSObject and autocompletion becomes so much easier because you don't have to search through all the inherited methods and properties from the base object. Apple could have instead created an appContext property on NSObject and every object would be able to get to the appContext easily. I think their choice of the Application singleton was a better fit.

Instance Manager Pattern

In this pattern another class manages object instances as opposed to the singleton that manages itself and is the point of access.

Compose off of UIApplication delegate singleton

Since there is already an application delegate singleton due to the way Apple has designed it, you could have a single instance owned by the AppDelegate. With this design you would heavily use the singleton already there and just build off it and have the AppDelegate be the access point to other classes needed application wide.

Are singletons really bad?

As an iOS developer if I find myself doing a lot of designs or patterns that are not the standard Apple way of doing it then I usually find myself fighting Apple's APIs. MVC, KVC, KVO, delegation, and singleton patterns are just some of the more popular patterns used by Apple. Because we are building on top of those patterns it is best served to follow their lead. Think about it, why does Apple put the Core Data code in the AppDelegate? Because you are only supposed to use the one managedObjectContext. So, if you want to create your own data access interface then it would probably work best as a singleton as intended.

I can't speak for other programming languages, but from what I've researched singletons have a bad reputation. One of their big arguments is tight coupling. If you change the singleton's interface then it has to be changed in every place that was called througout the entire program. This is a valid argument. Make sure your singleton's outer interface is designed right. The implementation part is easy to change at any time. For example, the underlying data store can be completely encapsulated in the singleton and the real backing store should be able to be swapped out without changing anything else but the singleton itself.

Its just a matter of managing complexity. The alternatives I listed above to singleton patterns have their own complexity problems. The complexity never goes away, it just gets shifted so take your pick of what you want the complexity to be. I myself do see valid use cases for the singleton pattern.

Specific examples where I've considered using a singleton

MusicPlayer

If the requirement is to have music controls in various places of the app a singleton makes sense. IMO, this is a perfect example of the outer interface never having to change. musicPlayer.play(), musicPlayer.pause(), musicPlayer.skipForward(), musicPlayer.skipBackward(), musicPlayer.isPlaying

DataAccess

Accessing database data is a good candidate since it usually needs to happen all over the place.

GameCenter

You might want to wrap GameKit and as a single GameCenter object might be a good fit since there is only a single GKLocalPlayer

AppPurchaser

StoreKit has a rough API and a lot of notifications to handle which could be handled by a singleton.

GroupDataAccess

Are you using app groups for a today extension or Apple watch extension? You can provide 1 single interface that is a member of both targets that will allow you access to the same Core Data store and/or NSUserDefault suite.

A good rule of thumb

You should be able to prefix your singleton class name with "App" and it would still make sense. ie: AppDataAccess, AppMusicPlayer, etc

Conclusion

It will be interesting to see how much Apple continues to use the singleton pattern with Swift and a more functional style of programming. It has been a frequently used pattern in Objective-C. For now I will continue to use it until Apple's Swift libraries grow and we see if there are new patterns they decide to use.

Date: 2015-01-07T02:51+0000

Author: Korey Hinton

Org version 7.9.3f with Emacs version 24

Validate XHTML 1.0