Swift Access Control
Access control decides which parts of your code other files and other modules may see. Good programs expose a small, clear surface and hide the messy details. Hidden details change freely without breaking the code that depends on the visible parts.
Picture an office building. Visitors enter the lobby, employees use the working floors, and only the manager opens the vault. Swift offers five access levels that follow the same idea.
Diagram: Rings of Access
+---------------------------------------------+
| open / public : anyone, any module |
| +---------------------------------------+ |
| | internal : same module (default) | |
| | +---------------------------------+ | |
| | | fileprivate : same file only | | |
| | | +---------------------------+ | | |
| | | | private : same scope only | | | |
| | | +---------------------------+ | | |
| | +---------------------------------+ | |
| +---------------------------------------+ |
+---------------------------------------------+The Five Access Levels
openallows use and subclassing from any module. Classes and class members only.publicallows use from any module without outside subclassing.internalallows use inside the same module. Swift applies this level when you write nothing.fileprivateallows use inside the same source file.privateallows use inside the same declaration and its extensions in the same file.
A module is one unit of code, such as an app or a framework. Most beginner apps use a single module. The internal level covers nearly everything.
Private Members
Mark internal workings as private. Outside code cannot touch them.
class BankAccount {
private var transactionLog: [String] = []
private(set) var balance: Double = 0
func deposit(_ amount: Double) {
guard amount > 0 else { return }
balance += amount
log("Deposit \(amount)")
}
private func log(_ entry: String) {
transactionLog.append(entry)
}
}
let account = BankAccount()
account.deposit(100)
print(account.balance) // Output: 100.0
// account.balance = 5000 // Error: setter is private
// account.log("Hack") // Error: method is privateThe modifier private(set) lets everyone read balance and allows only the class itself to change it. This pattern protects important values from careless edits.
Fileprivate Members
Use fileprivate when several types in one file must cooperate while the rest of the app stays out.
fileprivate func formatPrice(_ value: Double) -> String {
"$" + String(format: "%.2f", value)
}
struct Invoice {
let amount: Double
var label: String { formatPrice(amount) }
}
print(Invoice(amount: 49.5).label) // Output: $49.50The call to String(format:) needs import Foundation in a standalone file.
Public and Open for Frameworks
Developers who build frameworks and packages mark their outward-facing types as public. Everything else stays internal.
public struct Greeter {
public init() {}
public func hello(_ name: String) -> String {
"Hello, \(name)"
}
}A public struct needs a public initializer because the automatic initializer stays internal. The difference between open and public shows up only with classes: outside modules can subclass and override open members and cannot override public members.
Access Rules to Remember
- A public function cannot use a private type as a parameter or return value.
- A function never has a higher access level than the types it uses.
- Members of a private type default to private or fileprivate behavior.
Choosing a Level
Start with the most restrictive level and open a member only when another part of the code needs it. Restrictive defaults keep the design tidy and keep bugs close to their source.
Common Mistakes
Beginners often mark every member as public out of habit. Open doors invite misuse, and every public member becomes a promise you must keep for years. Hide members until a real need appears.
Another mistake involves forgetting the access level of an initializer. A public struct still hides its automatic initializer, and other modules fail to create the struct. Write a public initializer by hand.
Real-World Use
Banking apps, game scoreboards, and shopping carts all guard their balances and totals with private(set). The screen reads the value and only the model changes it. Bugs shrink because one small section of code owns every change.
Key Takeaways
- Swift offers
open,public,internal,fileprivate, andprivate. - The default level is
internal. private(set)allows public reading with restricted writing.- Hide details by default and share only what other code needs.
