Access Control
Prerequisites: Structs, Classes and reference types
Access control decides which code can see and use a type, property, or method. It lets you hide implementation details, so they can be changed without breaking code that depends on them. This idea is called encapsulation.
The levels
| Level | Visible to |
|---|---|
private |
The enclosing declaration (and its extensions in the same file) |
fileprivate |
Anything in the same file |
internal |
Anything in the same module — the default |
package |
Other modules in the same package (Swift 5.9+) |
public |
Any module that imports yours |
open |
Like public, and classes can be subclassed outside the module |
A module is one unit of code that's built together — an app, or one library target in a Swift package.
Example
struct PasswordValidator {
private let minimumLength = 8
func isStrong(_ password: String) -> Bool {
isLongEnough(password) && hasDigit(password)
}
private func isLongEnough(_ password: String) -> Bool {
password.count >= minimumLength
}
private func hasDigit(_ password: String) -> Bool {
password.contains { $0.isNumber }
}
}
let validator = PasswordValidator()
print(validator.isStrong("abc12345"))
print(validator.isStrong("password"))
true
false
isStrong is the type's interface. The private helpers can be renamed
or rewritten freely — nothing outside can depend on them. Calling
validator.hasDigit("x") from outside is a compile error:
'hasDigit' is inaccessible due to 'private' protection level.
Read-only from outside
private(set) allows reading everywhere but writing only inside the
type — you saw it in the Methods lesson:
struct Account {
private(set) var balance = 0
}
In apps and libraries
In an app, the default internal is usually fine; use private for
helpers. In a library others import, only types marked public are
visible, so you choose exactly what the library promises to support.
Common mistakes
- Making everything public "just in case". Every public member is a promise to keep it working.