Private
The private access modifier restricts access to the entity it is defined in, as well as any nested type within it — also known as the “lexical scope”.
To demonstrate, continue with your banking library by extending the behavior of
BasicAccount to make a CheckingAccount:
class CheckingAccount: BasicAccount {
private let accountNumber = UUID().uuidString
class Check {
let account: String var amount: Dollars
private(set) var cashed = false
func cash() { cashed = true
}
init(amount: Dollars, from account: CheckingAccount) { self.amount = amount
self.account = account.accountNumber
}
}
}
CheckingAccount has an accountNumber which is declared private. CheckingAccount also has a nested type Check which can read the private value of accountNumber in its initializer.
Note: In this example, the UUID class is used to generate unique account numbers. This class is part of Foundation, so don’t forget to import it!
Checking accounts should be able to write and cash checks as well. Add the following methods to CheckingAccount:
func writeCheck(amount: Dollars) -> Check? { guard balance > amount else {
return nil
}
let check = Check(amount: amount, from: self) withdraw(amount: check.amount)
return check
}
func deposit(check: Check) { guard !check.cashed else {
return
}
deposit(amount: check.amount) check.cash()
}
While CheckingAccount can still make basic deposits and withdrawals, it can now also write and deposit checks! The method writeCheck(amount:) checks for sufficient balance before withdrawing the amount and creating the check, and deposit(check:) will not deposit the check if it has already been cashed.
Give this code a try in your playground by having John write a check to Jane:
// Create a checking account for John. Deposit $300.00 let johnChecking = CheckingAccount() johnChecking.deposit(amount: 300.00)
// Write a check for $200.00
let check = johnChecking.writeCheck(amount: 200.0)!
// Create a checking account for Jane, and deposit the check. let janeChecking = CheckingAccount() janeChecking.deposit(check: check)
janeChecking.balance // 200.00
// Try to cash the check again. Of course, it had no effect on
// Jane's balance this time :] janeChecking.deposit(check: check) janeChecking.balance // 200.00
This code works great, of course; the real story is what this code can’t do. Remember that access control lets you control the interface to your code. Look at what the autocomplete window shows as the interface for CheckingAccount:
The accountNumber is treated as an implementation detail of CheckingAccount, and isn’t visible to consuming code.
Likewise, Checking makes the setter for cashed private and requires consumers to use cash() instead:
This interface gives Check a way for consumers to mark a check as cashed, but not the other way around! In other words, it is not possible to un-cash a check.
Finally, even though accountNumber was not visible on CheckingAccount, the number is made accessible by anyone holding a Check:
While the account property got its value from the CheckingAccount, that’s but another implementation detail. The important thing is that access modifiers let the code shape its own interface regardless of the code used to implement it.