Property Observers
Part of the Object Oriented Programming section of Coddy's Swift journey. Lesson 13 of 57.
A property observer runs code whenever a stored property changes. didSet runs right after the new value is stored, and inside it oldValue holds the previous value:
class Player {
var score = 0 {
didSet {
print("score \(oldValue) -> \(score)")
}
}
}
let p = Player()
p.score = 10
p.score += 5Output:
score 0 -> 10
score 10 -> 15willSet runs before the change, with the incoming value as newValue. Observers do not run when the property gets its initial value:
struct Thermostat {
var target = 20 {
willSet { print("changing to \(newValue)") }
didSet { print("changed from \(oldValue)") }
}
}
var t = Thermostat()
t.target = 22Output:
changing to 22
changed from 20didSet is a good place to keep a value valid. Assigning to the property inside its own didSet does not trigger the observer again:
struct Volume {
var level = 5 {
didSet {
if level > 10 { level = 10 }
if level < 0 { level = 0 }
}
}
}
var v = Volume()
v.level = 42
print(v.level)
v.level = -3
print(v.level)Output:
10
0Observers can also keep a history or update related state, which keeps that logic next to the property instead of scattered through the code:
class Account {
var history: [Int] = []
var balance = 0 {
didSet { history.append(balance - oldValue) }
}
}
let a = Account()
a.balance = 100
a.balance = 70
print(a.history)Output:
[100, -30]Challenge
EasyComplete the class Tank. Its stored property level must stay between 0 and capacity: add a didSet that corrects values outside that range, and that appends a warning low to alerts whenever the corrected level ends below 20% of the capacity (and the previous level was not low).
The supplied code reads the capacity and then changes +n or -n. It prints the level after each change and finally alerts: with the number of warnings.
Your code goes in Tank.swift. main.swift holds the supplied input/output code and cannot be edited.
Try it yourself
// Supplied input/output code: keep it as it is
var input: [String] = []
while let line = readLine() { input.append(line) }
let tank = Tank(capacity: Int(input[0])!)
for cmd in input.dropFirst() {
let n = Int(cmd.dropFirst())!
tank.level += cmd.hasPrefix("+") ? n : -n
print(tank.level)
}
print("alerts: \(tank.alerts.count)")
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesMethods and selfInitializersClasses Are ReferencesRecap - Library Card4Encapsulation
Access ControlRead-Only From OutsideGuarding StateFailable InitializersRecap - Bank Account7Polymorphism and Generics
PolymorphismType CastingGeneric FunctionsGeneric TypesRecap - Shape Calculator5Inheritance
SubclassesOverriding MethodsInitializers and superfinal and HierarchiesRecap - Employee Hierarchy11Project: Library Management
Books and MembersBorrowing Books3Properties in Depth
Computed PropertiesProperty ObserversStatic MembersEnums with MethodsRecap - Temperature LogPractice on your own: Swift playground