When access levels were first introduced in Swift, there was some confusion and criticism about them. While developers were excited about the addition of access control to the Swift programming language, the behavior of the private
keyword was different from that of other programming languages.
This has changed in Swift 3 by the addition of another keyword for private access control, fileprivate
. The difference is subtle but easy to understand.
Before Swift 3
Before the introduction of Swift 3, the private
keyword limited the use of entities (classes, structures, enumerations, ...) to the source file in which they were defined. Take a look at the example below.
import UIKit
class NotesViewController: UIViewController {
private var dataSource = [String]()
}
extension NotesViewController: UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
...
}
We declare a UIViewController
subclass, NotesViewController
, with a private property, dataSource
, of type [String]
. In the same source file, we create an extension for the NotesViewController
class to conform it to the UITableViewDataSource
protocol. What is interesting is that we can access the dataSource
property in the extension even though the property is declared private.
Swift 3
If we port the above code snippet to Swift 3, the compiler throws an error. It tells us that the dataSource
property is an unresolved identifier. In other words, by declaring the dataSource
property as private, it is not accessible in the extension.
import UIKit
class NotesViewController: UIViewController {
private var dataSource = [String]()
}
extension NotesViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
...
}
There's Private and Private
Swift 3 introduces the fileprivate
keyword that replaces the private
keyword. You can try this out by marking the dataSource
property as fileprivate
instead of private
.
import UIKit
class NotesViewController: UIViewController {
fileprivate var dataSource = [String]()
}
extension NotesViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
...
}
As the name implies, the fileprivate
keyword limits access to the entity to the source file it is declared in. We can verify this by moving the extension for the NotesViewController
class to a separate file. This results in the same error we encountered earlier.
import UIKit
extension NotesViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
}
What About Private
You can still use the private
keyword for access control. The Swift team has listened to the feedback of the community and, as a result, the meaning of the private
keyword is now similar to that in many other programming languages. An entity that is declared private can only be accessed within the lexical scope it is declared in. Euh. What?
To better understand this, we need to revisit the previous example. The extension for the NotesViewController
class was unable to access the dataSource
property if we marked it as private
, even if we added the extension to the same source file in which the NotesViewController
class was declared. Why is that?
The dataSource
property is only accessible from within the NotesViewController
class. As a rule of thumb, when using the private
keyword, the entity is only accessible within the set of curly braces it is declared in.
Is This Better?
While private
and fileprivate
may be confusing at first, I am very glad that private
now has a clearer, more intuitive meaning. The original implementation of the private
keyword confused many developers, especially those coming from other programming languages. By renaming private
to fileprivate
, this is no longer the case. The name clearly indicates that an entity is private to a particular (source) file.
Even though the Swift evolution proposal mentions that this change enables developers to spread the implementation of entities across multiple files, this doesn't seem to be possible at the moment. My hope is that this changes in the future.