38
\$\begingroup\$

I am pretty new to Swift, as a solo developer I was wondering if somebody could pass comments on the singleton implementation below. The code does work, but being new to Swift and knowing that there are a lot of gotchas, I wanted to get some feedback from someone more experienced.

// Test Singleton Swift 1.2
class FGSingleton {
    static var instance: FGSingleton!
    var gameScore: Int = 0

    // SHARED INSTANCE
    class func sharedInstance() -> FGSingleton {
        self.instance = (self.instance ?? FGSingleton())
        return self.instance
    }

    // METHODS
    init() {
        println(__FUNCTION__)
    }
    func displayGameScore() {
        println("\(__FUNCTION__) \(self.gameScore)")
    }
    func incrementGameScore(scoreInc: Int) {
        self.gameScore += scoreInc
    }
}

Tests:

FGSingleton.sharedInstance().displayGameScore()
FGSingleton.sharedInstance().incrementGameScore(100)
FGSingleton.sharedInstance().incrementGameScore(1)
FGSingleton.sharedInstance().displayGameScore()
FGSingleton.sharedInstance().incrementGameScore(1000)
FGSingleton.sharedInstance().displayGameScore()

Output:

init()
displayGameScore() 0
displayGameScore() 101
displayGameScore() 1101
\$\endgroup\$
25
\$\begingroup\$

You could simplify the above significantly:

class FGSingleton {
    static let sharedInstance = FGSingleton()

    var gameScore: Int = 0

    // METHODS
    private init() {
        println(__FUNCTION__)
    }
    func displayGameScore() {
        println("\(__FUNCTION__) \(self.gameScore)")
    }
    func incrementGameScore(scoreInc: Int) {
        self.gameScore += scoreInc
    }
}

And call it like this:

FGSingleton.sharedInstance.displayGameScore()
\$\endgroup\$
  • \$\begingroup\$ Works fine with Swift 1.2, just a quick note the sharedInstance should be declared as static let sharedInstance = FGSingleton() \$\endgroup\$ – fuzzygoat Feb 12 '15 at 11:22
  • \$\begingroup\$ You are right. I have removed the class keyword. \$\endgroup\$ – Rhuantavan Feb 12 '15 at 12:28
  • 2
    \$\begingroup\$ Note, not only is this simpler, but the implementation in the original question is not thread-safe, whereas this is (because statics are instantiated lazily with dispatch_once). \$\endgroup\$ – Rob Feb 19 '15 at 19:11
  • 1
    \$\begingroup\$ Shouldn't the initializer be private to make sure only one instance of FGSingleton can be created? \$\endgroup\$ – Apfelsaft Apr 29 '15 at 8:32
  • \$\begingroup\$ A singleton defined with this pattern that subclasses NSObject is actually deadlocking for me in Swift 2.0. However, the only way I currently have to reproduce is to add a breakpoint that logs data from the shared instance on the same line where the shared instance is first referenced in code. \$\endgroup\$ – Michael Johnston Oct 2 '15 at 1:49
18
\$\begingroup\$

First and foremost, printing text to the console is absolutely pointless for an iOS application. It's okay to do it as an easy way to test whether our code is working as intended, but we don't want to mistakenly leave this in the final release build.

So, step 1: Follow the instructions in this StackOverflow post to set up a custom compiler flag for your Swift project.

Step 2: Wrap all of your println statements in #if DEBUG and #endif.

Example:

    init() {
#if DEBUG
        println(__FUNCTION__)
#endif
    }

Now our debug builds can print useful debug statements, and we won't accidentally forget to remove them when it comes time to compile our final release build. Perhaps more importantly, we won't have to waste time putting them all back in after we make the final release build and start adding features or fixing bugs.

I highly recommend either making a function to do this debug printing:

    func debugPrintln(s: String) {
#if DEBUG
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "HH:mm:ss.zzz"
        let timeStamp = dateFormatter.stringFromDate(NSDate())
        println("[" + timeStamp + "]: " + s)    
#endif
    }

Or just make an Xcode code snippet out of the following:

#if DEBUG
    println(<#string#>)
#endif

And set the autocomplete to "println"

enter image description here

By doing this, any time you type "println", Xcode will try to autocomplete into the above snippet, and "string" within the parenthesis will be selected and ready to be overwritten by your typing. I have a similar one for Objective-C if you're interested:

enter image description here


Now then, let's review the stuff you've more directly asked questions about...

First, we don't actually need a class method to get the shared instance. I would go with Rhuantavan's static let suggestion.

But, let's pay attention to our access modifiers.

Our gameScore instance variable and our incrementGameScore instance method have the same access level, so what's the point?

Why would I write this:

FGSingleton.sharedInstance.incrementGameScore(3)

When I could just write this:

FGSingleton.sharedInstance.gameScore += 3

Moreover,

FGSingleton.sharedInstance.incrementGameScore(-3)

seems a little awkward versus:

FGSingleton.sharedInstance.gameScore -= 3

but it gets even worse if we compare setting the score to a specific number.

let targetScore = 5
let currentScore = FGSingleton.sharedInstance.gameScore
FGSingleton.sharedInstance.incrementGameScore((-1 * currentScore) + targetScore)

versus simply:

FGSingleton.sharedInstance.gameScore = targetScore

I'm not recommending what's best. It's certainly very possible that you don't want the user modifying the game score directly. But if that's the case, make the gameScore property a private variable, and implement more methods plus more safety checking on what's actually happening in these methods. Or if you don't mind them modifying the game score directly, just eliminate the unnecessary incrementGameScore method entirely.


Generally speaking, in the few instances when we actually want a singleton pattern, the class doesn't work out that great when people instantiate other instances of the object. The class is designed with the assumption that it will always be used as a singleton. Too often though, developers make the mistake of not putting in any safeguards to prevent the object from being instantiated.

Let's not make that same mistake.

In Swift, it's as simple as marking the init method as private:

private init() {}

Now if we try instantiating an object of our class in any other Swift file, we'll get a nasty error:

enter image description here

(I named my class SwiftSingleton)

This is what we want. If it's a singleton, we probably don't want people to have other instances available... so don't assume they won't try. It's easy enough to completely prevent it!


As some final parting food for thought, we really need to delve far more deeply into why we're making a singleton class at all. In all of my professional experience as an iOS developer, I've never needed to use a singleton, that I can recall.

When I was a less experienced programmer, I felt that a singleton was the answer to my problems of trying to pass data between various objects (or more likely, between view controllers). But it's not the right solution.

Do you actually need this singleton? Would it not be better to just have some sort of GameScore class that gets passed around among the objects that need to modify or read the data that the GameScore class holds?

In my opinion, most use cases of singletons are the lazy approach to actually learning how to pass data between your objects appropriately.

\$\endgroup\$
  • 1
    \$\begingroup\$ "...and we won't accidentally forget to remove them when it comes time to compile our final release build." Because println, unlike NSLog, doesn't write to the system log, this concern seems a lot less critical. Don't get me wrong: I'd still take it out because it's silly to include in production code, but it's not nearly the same problem that NSLog was. \$\endgroup\$ – Rob Feb 19 '15 at 19:16
  • \$\begingroup\$ @Rob, good information. can you share some links where I can read more about this please? \$\endgroup\$ – thesummersign Jul 9 '15 at 13:36
  • \$\begingroup\$ Re println performance and not writing to log, it's been mentioned in a number of WWDC videos, but I'm traveling and don't have the precise reference in front of me. \$\endgroup\$ – Rob Jul 9 '15 at 13:56
  • \$\begingroup\$ This is an excellent answer. As a newcomer to iOS development this really helped me learn about Singletons and how to effectively use the debugger. \$\endgroup\$ – damianesteban Jul 18 '15 at 17:09
4
\$\begingroup\$

The other answers have already commented on other aspects of the code; I'd simply like to leave here my preferred way of implementing singletons in Swift:

public let FGSingleton = FGSingletonClass()

public class FGSingletonClass {
    private init() {}
    // Etc...
}

To use the singleton:

let v = FGSingleton.myProperty

As you can see, there's no need for writing sharedInstance all the time, and you still get thread-safety and lazy instantiation for free. The only gotcha is that you have to slightly change the name of the class, because the variable and the class must have different names (I personally add Class to the end).

\$\endgroup\$
1
\$\begingroup\$

Most of the other answers cover everything in a very detailed and accurate manner but I want to highlight some of the practices that I liked to preach when it comes to singleton classes.

I used to create a singleton and then all the public methods in the class were static. These static methods called the then call the replica instance methods using the private sharedInstance

As its a singleton all my method calls are going to be using shared instance ONLY so why specify it again and again.

class FGSingleton {
private static let sharedInstance = FGSingleton()
private var gameScore: Int = 0

// METHODS
private init() {
    println(__FUNCTION__)
}
class func displayGameScore() {
    println("\(__FUNCTION__) \(self.sharedInstance.gameScore)")
}
class func incrementGameScore(scoreInc: Int) {
    self.sharedInstance.gameScore += scoreInc
}
}

and call will be simply

  FGSingleton.incrementGameScore(5)
  FGSingleton.displayGameScore()

UPDATE Just for a variation if you need to initialize some member variables in your singleton. You can do something like :

    public static let sharedInstance: FGSingleton = {
    let objBirthDate: NSDate = NSDate()
    return FGSingleton(date: objBirthDate)
}()

required private init(data: NSDate){...}
\$\endgroup\$
  • 1
    \$\begingroup\$ If this were an appropriate approach, why doesn't Apple do it with their plethora of singletons that we use everyday? \$\endgroup\$ – nhgrif Jul 10 '15 at 13:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.