How to Use Property Observers in Swift
What you’ll build or solve
You’ll add didSet and willSet to stored properties so your code can react to assignments automatically.
When this approach works best
Property observers work best when:
Learn Swift on Mimo
- You want to react to changes right where the property is defined, like recording changes for debugging.
- You need a quick hook to trigger a small side effect when a value updates.
- You want to inspect an incoming value before saving it, or compare old and new values.
This is a bad idea when the work is expensive, async, or full of business rules. In that case, a method call or a separate type usually keeps the code easier to follow.
Prerequisites
- Xcode or a Swift Playground
- Basic knowledge of Swift stored properties (
var) in structs or classes
Step-by-step instructions
Step 1: Use didSet to react after a value changes
didSet runs after Swift stores the new value. You can read the previous value using oldValue.
Swift
structScoreBoard {
varscore:Int=0 {
didSet {
print("Score changed from \(oldValue) to \(score)")
}
}
}
What to look for
oldValueis available automatically insidedidSet.scorealready contains the new value insidedidSet.
Step 2: Use willSet to react before a value changes
willSet runs before Swift stores the new value. You can read the incoming value using newValue.
Swift
structTemperature {
varcelsius:Double=0 {
willSet {
print("About to change to \(newValue)°C")
}
}
}
What to look for
newValueis available automatically insidewillSet.- The property still contains the old value inside
willSet.
Step 3: Know what to look for with observers
Observers stay small and predictable when you remember a few rules and patterns.
What to look for
- You can combine both on one property when you want a “before and after” hook.
Swift
structPlayer {
varhealth:Int=100 {
willSet {
print("Health will change to \(newValue)")
}
didSet {
ifhealth<=0 {
print("Player defeated")
}
}
}
}
- Common uses: lightweight logging, simple UI refresh triggers, basic validation or clamping, and analytics counters.
- Reassigning inside
didSetcan be used for clamping in Swift. Swift does not recursively trigger the observer from that reassignment. - Observers work only with stored properties, not computed properties.
- Observers do not run during initialization when setting initial values.
- Observers work in both structs and classes, the syntax stays the same.
Examples you can copy
Example 1: Simple change log
structSettings {
varisDarkMode:Bool=false {
didSet {
print("Dark mode changed from \(oldValue) to \(isDarkMode)")
}
}
}
varsettings=Settings()
settings.isDarkMode=true
Example 2: Inspect incoming values
structBankAccount {
varbalance:Double=0 {
willSet {
print("Balance will become \(newValue)")
}
}
}
varaccount=BankAccount()
account.balance=50
account.balance=120
Example 3: Clamping with didSet
structVolume {
varlevel:Int=5 {
didSet {
iflevel<0 {level=0 }
iflevel>10 {level=10 }
}
}
}
varvolume=Volume()
volume.level=20
print(volume.level)
Common mistakes and how to fix them
Mistake 1: Using observers on a computed property
What you might do:
Swift
structExample {
vartotal:Int {
didSet {
print("Changed")
}
get {0 }
set { }
}
}
Why it breaks: Swift only allows observers on stored properties, and this is a computed property.
Correct approach:
Swift
structExample {
vartotal:Int=0 {
didSet {
print("Changed")
}
}
}
Mistake 2: Expecting observers to run during initialization
What you might do:
Swift
structUser {
varage:Int=0 {
didSet {
print("Age updated")
}
}
init(age:Int) {
self.age=age
}
}
Why it breaks: Swift does not run willSet or didSet while initializing stored properties.
Correct approach:
Swift
structUser {
varage:Int=0 {
didSet {
print("Age updated")
}
}
init(age:Int) {
self.age=age
print("Age set during init")
}
}
Troubleshooting
- If you see
Cannot use property observers with a computed property, switch to a stored property or move logic into the setter. - If your observer never runs, confirm you are assigning a new value after initialization, not only setting the initial value.
- If your observer prints unexpected values, check which observer you used.
willSetreadsnewValue,didSetreadsoldValue. - If code feels hard to follow, keep observer bodies short and move complex work into methods.
Quick recap
- Use
didSetto run code after a stored property changes. - Use
willSetto run code before a stored property changes. - Read
oldValueindidSetandnewValueinwillSet. - Observers apply to stored properties, not computed properties.
- Observers do not run during initialization.
- Keep observer logic small, move complex rules into methods.
Join 35M+ people learning for free on Mimo
4.8 out of 5 across 1M+ reviews
Check us out on Apple AppStore, Google Play Store, and Trustpilot