Within the context of your app, it can be helpful to think of everything that provides information or functionality to other parts of your app as an API. Access control is a powerful abstraction tool that allows you to hide the implementation details and control how your API is used by the outside world. For example, if some piece of information or functionality isn’t “need to know” by another part of your app, you can (and should) hide that. A well-groomed API is not only a joy to use, but it can also prevent misuse and programmer errors.
Note: Access control is built around the concept of modules and source files. A module is a distinct unit of code distribution, like an app or framework.
There are five access levels:
privatelimits access to enclosing declaration and its extensions within the same file.
fileprivatelimits access to current file.
internallimits access to current module.
publicallows access between modules and prevents subclassing.
openallows access between modules and allows subclassing.
You can also mark a class as
final to prevent it from being subclassed and help control how that object is used.
You can control access to:
- Constructs, like classes, structs, protocols, and enums.
- Elements, like properties and methods.
- Property setters.
Let’s look at a few examples of access control in action.
Using the organization tips from our Swift Tips: Organization article, we’ve just created an extension on your view controller to conform to the
UICollectionViewDelegate protocol. We implement the necessary protocol methods and, along the way, create a few helper methods to reduce boilerplate or redundant logic.
This is all standard, everyday stuff up to this point. Now, let’s ask ourselves a question: Does the rest of the module (or even this file) need to know about or have access to these helper methods?
If we mark these methods as
private, they will no longer be accessible outside of view controller and its extensions within the same file.
Let’s say that we want to create an read-only property in our API. Simply annotate the property like so:
fileprivate(set) var someProperty: SomeType. Inside of the source file, you’ll be able to get and set the property like normal. However, outside of the source file, this property will be read only.
This also works with the other access control levels. For example:
private(set)limits the setter to the owning class or struct and its extensions within the same file.
internal(set)limits the setter to the current module, so an external module that’s importing the current one will not be able to set this property.
It’s pretty common to create helper structs or objects to wrap up units of information that a class needs to pass around internally. Marking these helpers as `fileprivate` will now hide them away as unimportant implementation details.
If you don’t specify an access level, `internal` will be assumed. There are a few nuanced exceptions to this. Apple’s Access Control documentation has all of the details, but it essentially boils down to this quote from Apple: “No entity can be defined in terms of another entity that has a lower (more restrictive) access level.” For example, a property that has a type defined as `internal` cannot be made `public`.
Access control is an excellent tool for declaring the intent of your code while encapsulating away unimportant implementation details. This will help make your APIs easier and safer to use. As with most things, the best way to familiarize yourself with Swift’s access control features and understand when to apply them is through practice.
Next week, we’ll dive into early exits in Swift with a look at how they help reduce nesting and clearly signal what the desired and possible outcomes are.