Updated for Xcode 14.2
By default, Swift’s structs let us access their properties and methods freely, but often that isn’t what you want – sometimes you want to hide some data from external access. For example, maybe you have some logic you need to apply before touching your properties, or maybe you know that some methods need to be called in a certain way or order, and so shouldn’t be touched externally.
We can demonstrate the problem with an example struct:
struct BankAccount {
var funds = 0
mutating func deposit(amount: Int) {
funds += amount
}
mutating func withdraw(amount: Int) -> Bool {
if funds >= amount {
funds -= amount
return true
} else {
return false
}
}
}
That has methods to deposit and withdraw money from a bank account, and should be used like this:
var account = BankAccount()
account.deposit(amount: 100)
let success = account.withdraw(amount: 200)
if success {
print("Withdrew money successfully")
} else {
print("Failed to get the money")
}
But the funds
property is just exposed to us externally, so what’s stopping us from touching it directly? The answer is nothing at all – this kind of code is allowed:
account.funds -= 1000
That completely bypasses the logic we put in place to stop people taking out more money than they have, and now our program could behave in weird ways.
To solve this, we can tell Swift that funds
should be accessible only inside the struct – by methods that belong to the struct, as well as any computed properties, property observers, and so on.
This takes only one extra word:
private var funds = 0
And now accessing funds
from outside the struct isn’t possible, but it is possible inside both deposit()
and withdraw()
. If you try to read or write funds
from outside the struct Swift will refuse to build your code.
This is called access control, because it controls how a struct’s properties and methods can be accessed from outside the struct.
Swift provides us with several options, but when you’re learning you’ll only need a handful:
private
for “don’t let anything outside the struct use this.”fileprivate
for “don’t let anything outside the current file use this.”public
for “let anyone, anywhere use this.”There’s one extra option that is sometimes useful for learners, which is this: private(set)
. This means “let anyone read this property, but only let my methods write it.” If we had used that with BankAccount
, it would mean we could print account.funds
outside of the struct, but only deposit()
and withdraw()
could actually change the value.
In this case, I think private(set)
is the best choice for funds: you can read the current bank account balance at any time, but you can’t change it without running through my logic.
If you think about it, access control is really about limiting what you and other developers on your team are able to do – and that’s sensible! If we can make Swift itself stop us from making mistakes, that’s always a smart move.
Important: If you use private
access control for one or more properties, chances are you’ll need to create your own initializer.
SPONSORED From March 20th to 26th, you can join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.