Defeating the Anti-Pattern Bully 💥 Part 1 - Singletons


It's actually pretty funny that I'm writing about this right now since I JUST wrote about the correct way to write a singleton in Swift. I've written quite a bit about the issues with our current Object-Oriented thoughts on managing state; singletons, you must remember, are not the exception.

You may have noticed the "Part 1" in my post's title. This post is going to be the inaugural post in my Anti-Pattern series. What I am going to try to achieve in this series is to show you (in sparse cases) how to implement these anti-patterns in a non anti-pattern-y way. That being said, you will also see some posts in this series staunchly oppose some anti-patterns. This is for good reason. Some anti-patterns need to get got.

So...Singletons.

After I posted my last post, I got some entertaining comments on Twitter. The gist of most of these Tweets were to not use Singletons. * "Singletons are evil!". * "Stay away!". * "Why write a post about how to write a singleton? You're encouraging others to use them when they shouldn't!".

For the most part I agree. However, there are some cases where it's needed. Even Apple lists singletons as a Cocoa Core Competency! BUT, even though it's a "Cocoa Core Competency", we should aim to not abuse this pattern and stay away from using it just because we can. For clarification, I don't take an extremist approach to using singletons. In this post, I hope to teach you where to use singletons, and when to stay away from singletons. To do so, I will use 2 differing examples.

Should I Stay? (AKA "The Reasonable Singleton")

The argument for the case against singletons is staggering. There really aren't many places you should use singletons and to be honest, for the past week I have had a HELL of a time trying to find a good place to use them. Days of research later, I ask NatashaTheRobot where it makes sense to use them and she pointed out to me that certain caches in iOS are a perfect place to use singletons! After our convo, I was bashing myself over the head because I've actually implemented these kinds of caches plenty of times in my career.

In iOS, Cocoa's NSFormatter classes are notorious for having large overhead. This means they be SUPA slow and suck up lots of resources to create. Singletons are actually suggested by Apple when using formatters in order to make your app more performant. Check this out:

Creating a date formatter is not a cheap operation. If you are likely to use a formatter frequently, it is typically more efficient to cache a single instance than to create and dispose of multiple instances. One approach is to use a static variable.
— Apple Developer Documentation

I would have to say that I definitely agree that this is a perfect reason to use a singleton. It's simple, it's pretty, and as far as I can tell, it doesn't really create any of the problems you would normally have with singletons in general. I like to call this The Reasonable Singleton:

class DateFormatterCache {
    static let sharedFormatter = DateFormatterCache()
    static let cache = Dictionary<String : NSDateFormatter>()
    private init() {}

    func cachedFormatter(#format: String) -> NSDateFormatter {
        if let cachedFormatter = cache[format] {
            return cachedFormatter
        } else {
            let newFormatter = NSDateFormatter()
            newFormatter.dateFormat = format
            cache[format] = newFormatter

            return newFormatter
        }
    }
}

The general consensus on why Singletons are bad are due to managing global state. In the formatter scenario, there isn't gloabl state. @cmajordev on Twitter couldn't have said it better: Caching expensive objects in memory via Singleton is justifiable since we aren't managing state, just an instance.

The Reasonable Singleton doesn't introduce tight coupling into your application. Other classes don't care if there is anything in your cache, since they could do with or without the added performance. The added performance is a lovely side effect. The bad side effect is really just a neutral side effect in this scenario. Since a class would've just created a new formatter themselves to begin with, race conditions aren't an issue! If two threads end up creating two of the same formatter, it doesn't matter. Multiple formatters shouldn't affect state in your app. If it does, then you have a separate issue with your app's architecture. Memory issues can be managed as well when receiving a memory warning since you can use a didReceiveMemoryWarning notification to clear your cache.

Does this sound familiar? It should; This is almost exactly how Apple's NSCache works. So why not just use NSCache? The answer is because creating your own singleton for formatter caching makes it very clear what you are doing. It's specific, it's modular, and best of all it has one responsibility - caching formatters.

Or Should I Go? (AKA "The Lazy Singleton")

Our problem with singletons is our abuse of them. Due to their ease of use, we find that it's easier to access values in singletons across multiple areas of our applications. Take a common example: the User singleton:

class User {
    var signInPreference: AuthenticationMethod = .TouchID

    static let defaultUser = User()
    private init() {}

    override func changePassword(newPassword: String) {
        UserAPI.updatePassword(newPassword) { success in
            //do all the things
        }
    }
}

I have seen this in many apps in my career. Hell, even I have done it on a tight deadline. A user signs in to the app, and then we create a singleton representation of that user so we can make it easy to sign them out later. Of course, there are other things you may need like User persistence and the like, but making a User a singleton can present quite a few problems that most of us don't realize exist at first glance:

  • Confusing Object Dependency Graphs - In our example, a defaultUser is a pretty simple implementation of a place many iOS Devs would use a singleton. One problem we immediately find is that, given a mature code base, any incoming developer would not be able to see what objects depend on the User singleton. When we first look through the classes of a codebase, we immediately look at each class' initializers and properties. If your class depends on another class and you don't see that in your initializers or properties, chances are you're doing it wrong.
  • They are Hard to Test - Singletons are notoriously hard to unit test. Especially when you take into consideration test theory. Unit tests are just that - units. Units are independent. Units are modular. Units are functional. Units are stateless.
    • They Create Tight Coupling
  • Singletons, by nature, are so full of state that they are global. They are dependent on outside classes to maintain that state, and many times they aren't modular. Due to their inherent global state, they create tight coupling between your unit tests. Testing functionality dependent on a singleton means you have to write your unit tests in a way that makes them dependent on each other for the sole purpose of replicating certain states in your test code. This inherently means your unit tests are no longer units. In our User example, access control can be a small example. Just to be able to test some portions of your singleton, you may have to change some fields from private to public so you can reset these fields after every test.
    • They Are Hard to Mock & Stub
  • Because true singletons can't be subclassed, they limit you to their single type when in use. This creates problems when mocking. A common abuse of singletons are API's that use them to fire off URL requests. You can easily write a test for a function that hides this URL code in it's body and have it fail since you have to wait for the request to finish. Plus you can't mock a specific line of code in the body of a function. If you wanted to stub out a changePassword() API request in your User singleton, you would need to override the change password function for your tests. Since you can't subclass, it would be difficult to stub out this function without doing magic like swizzling.
    • They Make You Write Code to Make Your Tests Pass
  • Due to their persistent state, it's pretty obvious that when writing tests, you have to be careful of the order in which they are written in order to make them pass. This hides bugs and yet again, takes the "unit" out of "unit testing".
    • They Make You Write Code Specifically For Your Tests
  • Because Test #1 can potentially change your User's state in a way to make Test #2 fail, you often have to create a purge() function specifically for your unit tests to make Test #2 pass. For one thing, purge() functions aren't used in your actual code so you end up writing code just to make a class testable. This is a pretty bad code smell in itself.
  • Concurrency Sucks - Multi threading in an application is hard enough without throwing global state into the equation. For one thing, singletons have to make sure their initialization is thrown into a dispatch_once queue. In many scenarios that I've been burned by, you have to be mindful that there isn't code in your application that can read/write from your singleton at the same time in multiple threads. And if there is code like this, you have to put locks around functions in your singleton to prevent race conditions and their ilk. This creates unnecessary code complication. In our User example, you could have the potential to sign out a user at the same time it tries to sign in. Which one will win out? Who knows!

Singletons that have the potential to introduce this behavior are what I like to call the "Lazy Singleton". I'm not actually calling you lazy if you use these yourself because even I still have them in my code! I call them "lazy" because as developers, we tend to take the easy way out to just make something work sometimes. It's my opinion that these singletons come out as a direct result of that and stay because they are sneaky enough to hide the bugs until production time :). Instead of passing around our User instance using dependency injection, we default to making it a singleton which can bring more problems than it's worth - making the "easy way" out harder than doing it correctly the first time around.

How to Stay Away - (AKA - "Dependency Injection")

This idea is not particularly novel but there is a way that you can replace your singletons by using dependency injection. Pass your reference around in your function parameters and initializers and use them when needed. For example, have your SignInViewController keep a settable User property:

class SignInViewController: UIViewController {
    var user: User?
    @IBOutlet weak var signInTextField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        signInTextField.text = user?.username
    }
}

Having something like this is a little extra work but enables much easier testing because you can now mock your User dependency in your tests!

import Quick
import Nimble

//This test uses Quick and Nimble in case you were wondering what's happening with the syntax of this test.
class SignInViewControllerSpec: QuickSpec {
    class MockUser: User {
        override func changePassword(newPassword: String) {
            //Let's skip the API call!
            password = newPassword
        }
    }

    override func spec() {
        var controller: SignInViewController!

        describe("handleChangePassword") {
            context("when a user attempts to change their password") {
                beforeEach {
                    let storyboard: UIStoryboard = UIStoryboard(name: "SignIn", bundle: NSBundle(forClass: self.dynamicType))
                    controller = storyboard.instantiateInitialViewController() as! SignInViewController
                    controller.user = MockUser() //Dependency Injection right here. BEHOLD THE POWER
                    controller.initiateChangePasswordAPICall("password456")

                    let _ = controller.view
                }
                it("should successfully change the password field in the User class") {
                expect(controller.user.password).to(equal("password456"))
            }
        }
    }
}

Conclusion

For the most part, Singletons are pretty bad. The introduce a lot of issues that you don't really see until it's too late. By the time you catch any of these issues, you'll most likely have a situation where a refactor that involves removing a singleton is out of the question. However, there are merits to using singletons and we should never rule out the possibility of their use just because everyone else says they are generally bad. Are singletons an anti-pattern? Sure, for the most part they are. It's up to us to determine the the time and place for them and use the "Reasonable Singleton" when it's appropriate to do so. Apple uses them, so why can't we? With good judgement, we can get rid of the "Lazy Singleton" once and for all!

Happy coding fellow nerds!