Private: It’s Not What You Think

So, here is an interesting tidbit for any of you Swifters that have experience in other languages with access modifiers: “private” doesn’t mean what you assume it means.

Put these to classes in the same file:

public class Secrets {
    private var shhh = "Cannot Hide"
}

public class Exposed : Secrets {
    public func CannotRun() -> String {
        return self.shhh // no compiler error?!
    }
}

 
And, call this:

// This has to be in the same file as the classes
Secrets().shhh // no compiler error?! value is "Cannot Hide"

// This, of course, can be put in a different file
Exposed().CannotRun() // returns "Cannot Hide"

 

Ok. That wasn’t what I was expecting. However, if we read the manual, we find the following: Private access restricts the use of an entity to its own defining source file. Use private access to hide the implementation details of a specific piece of functionality.

I have to admit that I didn’t discover this by reading Apple’s documentation. Actually, it happened by accident when I was playing in a playground. The thing about a playground is that it is just one file, which means that the “private” access modifier has no effect. So, when I made a class field private and was still able to access it, I assumed that there must be a bug in the playground. A quick search on Google straightened me out, though.

So, the next question I asked was “Why?” Why would you want “private” to scope variables, methods, and classes to the current file?

It just so happened that I needed a way to quickly create some fake instances of classes for testing. One of the requirements was that the faked instances should have unique IDs. Prior to Swift 1.2, the “static” keyword wasn’t available, so I came up with the following solution instead:

// UniqueItem.swift file

public class UniqueItem {
    public var id : Int
    
    init() {
        id = 0 // maps to unique database value
    }
}
// FakeUniqueItem.swift file

private var autoId: Int = 0

private func getNextId() -> Int {
    return autoId++
}

public class FakeUniqueItem : UniqueItem {
    override init(){
        super.init()
        id = getNextId()
    }
}
// MyTests.swift file
 
println(FakeUniqueItem().id) // 0
println(FakeUniqueItem().id) // 1
println(FakeUniqueItem().id) // 2

No “static”? No problem. Because both the autoId variable and getNextId() function were private, they weren’t accessible outside the file. I got the effect of a static variable because I was sharing the instance of autoId across instances of my FakeUniqueItem class.

Fortunately, the release of Swift 1.2 added static variables to the mix, so I was able to get rid of my global, private variable and function:

public class FakeUniqueItem : UniqueItem {
    private static var autoId : Int = 0
    
    override init(){
        super.init()
        id = FakeUniqueItem.autoId++
    }
}

So there you have it. My story of how I discovered what “private” means to Swift. If you can think of other ways to leverage it, please leave a comment!

Share