Swift 3 Access Controls

Swift 3 has brought us some interesting access control changes. The differences between open and public or private and fileprivate take some getting used to. Luckily unless you are writing a framework the new rules are simple. Here is what you need to know.

Note: Swift 4 has changed the scope of private access reducing the need to use fileprivate for extension access. See Swift 4 access levels for details.

The Five Access Levels of Swift 3

Swift 3 has five access levels that control from which source file or module you can access something. In order from most open to most restricted:

  • open you can access open classes and class members from any source file in the defining module or any module that imports that module. You can subclass an open class or override an open class member both within their defining module and any module that imports that module.

  • public allows the same access as open - any source file in any module - but has more restrictive subclassing and overriding. You can only subclass a public class within the same module. A public class member can only be overriden by subclasses in the same module. This is important if you are writing a framework. If you want a user of that framework to be able to subclass a class or override a method you must make it open.

  • internal allows use from any source file in the defining module but not from outside that module. This is generally the default access level.

  • fileprivate allows use only within the defining source file.

  • private allows use only from the enclosing declaration. This has changed in Swift 4 - see Swift 4 access levels.

Note that Objective-C classes and methods are now imported as open.

Don't Panic

It is worth remembering that an application is a module and that internal access is the default. This means application code you write is accessible from all source files in the application by default.

Unless you are writing a framework you probably only need to think about access levels when you want to restrict access to a single source file using fileprivate or private.

Using fileprivate and private

Let's look at some examples where we might want to use fileprivate and private. Suppose I have a view controller that has a property which I do not want accessed outside of the source file. In Swift 2 I would declare it as private and move on:

class RootViewController: UIViewController {
  private var someFlag = false
}

Unfortunately, using Swift 3, if I now try to access this property from a class extension, in the same source file, I hit a problem:

extension RootViewController: MyGreatDelegate {
  func doSomething {
    if someFlag {
      // do the thing
    }
  }
}

// Use of unresolved identifier 'someFlag'

The problem is that the private access level restricts access to the property to the enclosing class declaration. The extension is not allowed access even though it is in the same source file. The solution is to switch the access level to fileprivate:

class RootViewController: UIViewController {
  fileprivate var someFlag = false
}

Another example using private in extensions for methods that you do not want accessed outside of the extension. For example, helper methods for a delegate:

extension RootViewController: UITextFieldDelegate {
  func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    handle(text: textField.text)
    textField.resignFirstResponder()
    return true
  }

  // Not accessible outside of this extension
  private func handle(text: String?) {
    // do something
  }
}

Other Hints and Tips

Getters and Setters

You can give a setter lower access than its getter to limit the read-write access.

class MyDataSource {
  // property is read-only outside of this class definition
  private(set) var someFlag = false

  // ...
}

By default the property getter and setter in this example would have internal access. Making the setter have a private access level means we can only set it within the class definition. We cannot set it from another source file or from a class extension in the same source file. If we wanted to be able to set the property from an extension we would need to give the setter fileprivate access:

fileprivate(set) var someFlag = false

Unit Test Targets

A unit test target is its own module so does not have access to any types or properties in the application module that are by default internal. You give your tests internal access by importing with the @testable attribute:

import XCTest
@testable import MyDataSource
class MyDataSourceTests: XCTestCase {
  // func testSomething() {...}
}

Final - Preventing Overrides

The final keyword is not an access level but you can add it to any of the access levels (apart from open) to prevent subclassing or overriding. There is a potential performance improvement from this as the compiler can avoid dynamic dispatch though you can allow the compiler to infer this if you use Whole Module Optimization.

Further Reading