0
0
Swiftprogramming~15 mins

Property observers (willSet, didSet) in Swift - Deep Dive

Choose your learning style9 modes available
Overview - Property observers (willSet, didSet)
What is it?
Property observers in Swift are special blocks of code that run when a property's value is about to change or has just changed. They let you watch and respond to changes in a property's value without needing to write extra code every time you update it. The two main observers are willSet, which runs before the change, and didSet, which runs after. This helps keep your code organized and reactive to changes.
Why it matters
Without property observers, you would have to manually add extra code every time you change a property to track or react to its new value. This can lead to repeated code and mistakes. Property observers solve this by centralizing change handling, making your programs more reliable and easier to maintain. For example, updating a user interface automatically when data changes becomes simpler and less error-prone.
Where it fits
Before learning property observers, you should understand Swift properties and basic variable assignment. After mastering observers, you can explore Swift's computed properties, property wrappers, and reactive programming patterns that build on this concept.
Mental Model
Core Idea
Property observers are like automatic alarms that notify you right before and right after a property changes its value.
Think of it like...
Imagine a mailbox with two sensors: one that alerts you just before the mailbox door opens (willSet), and another that alerts you right after the door closes (didSet). These sensors help you react at the exact right moments without watching the mailbox constantly.
┌───────────────┐
│   Property    │
├───────────────┤
│   willSet     │ ← runs BEFORE value changes
│   (newValue)  │
├───────────────┤
│   Value Set   │
├───────────────┤
│   didSet      │ ← runs AFTER value changes
│   (oldValue)  │
└───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Swift Properties Basics
🤔
Concept: Learn what properties are and how to assign values to them in Swift.
In Swift, a property is a value associated with a class, struct, or enum. You can create a variable property like this: var age: Int = 25 You can change it anytime by assigning a new value: age = 30 This is the foundation before adding observers.
Result
You can store and update values in properties simply by assigning new values.
Knowing how properties hold and update values is essential before adding any automatic reactions to those changes.
2
FoundationIntroducing Property Observers Syntax
🤔
Concept: Learn the basic syntax of willSet and didSet observers in Swift properties.
You add observers inside a property like this: var score: Int = 0 { willSet { print("Score will change to \(newValue)") } didSet { print("Score changed from \(oldValue) to \(score)") } } Here, willSet runs before the value changes, and didSet runs after.
Result
When you assign a new value to score, messages print showing the change moments.
Understanding the syntax lets you hook into property changes without extra manual code.
3
IntermediateUsing willSet and didSet Parameters
🤔Before reading on: do you think willSet and didSet can access both old and new values? Commit to your answer.
Concept: Learn what values are available inside willSet and didSet and how to use them.
Inside willSet, you get a constant called newValue representing the value about to be set. Inside didSet, you get oldValue representing the previous value before the change. Example: var temperature: Double = 20.0 { willSet { print("About to change to \(newValue) degrees") } didSet { print("Changed from \(oldValue) to \(temperature) degrees") } } temperature = 25.0 Output: About to change to 25.0 degrees Changed from 20.0 to 25.0 degrees
Result
You can react precisely to what the new and old values are during changes.
Knowing which values are accessible helps you write meaningful reactions to property changes.
4
IntermediateObservers on Computed vs Stored Properties
🤔Before reading on: can you add willSet or didSet to computed properties? Commit to your answer.
Concept: Understand that property observers only work on stored properties, not computed ones.
Stored properties hold actual data, so observers can watch changes. Computed properties calculate values on the fly and don't store data, so observers don't apply. Example of computed property: var fullName: String { get { return "John Doe" } set { /* no storage */ } } You cannot add willSet or didSet here. Trying to do so causes a compiler error.
Result
Observers only work on properties that store data directly.
Recognizing this prevents confusion and compiler errors when trying to observe computed properties.
5
IntermediateUsing Observers in Classes and Structs
🤔
Concept: Learn that property observers work in both classes and structs, with some differences in behavior.
You can add observers to properties in classes and structs: struct Counter { var count: Int = 0 { didSet { print("Count changed from \(oldValue) to \(count)") } } } var c = Counter() c.count = 5 In classes, observers behave similarly, but remember classes are reference types, so changes affect shared instances.
Result
You can track property changes in both value and reference types.
Knowing this helps you decide where to place observers depending on your data model.
6
AdvancedAvoiding Infinite Loops in didSet
🤔Before reading on: do you think changing a property inside its own didSet causes infinite loops? Commit to your answer.
Concept: Understand that modifying a property inside didSet can cause repeated calls and how to prevent it.
If you assign to the same property inside didSet, it triggers didSet again, causing an infinite loop. Example (bad): var value: Int = 0 { didSet { value += 1 // triggers didSet again } } This causes a crash. To avoid this, use a separate variable or condition: var value: Int = 0 { didSet { if value < 10 { value += 1 // safe because of condition } } }
Result
Proper checks prevent infinite loops when changing properties inside observers.
Understanding this prevents common runtime crashes and subtle bugs in reactive code.
7
ExpertInterplay Between Observers and Property Wrappers
🤔Before reading on: do property observers run before or after property wrappers' code? Commit to your answer.
Concept: Explore how property observers interact with Swift's property wrappers and their execution order.
Property wrappers add extra behavior around properties. Observers run after the wrapper's setter. Example: @propertyWrapper struct Clamped { private var value: Int = 0 var wrappedValue: Int { get { value } set { value = min(max(newValue, 0), 100) } } } struct Example { @Clamped var score: Int = 0 { didSet { print("Score is now \(score)") } } } When you set score, the wrapper clamps the value first, then didSet runs with the clamped value. This order matters for correct logic.
Result
Observers see the final value after wrappers modify it.
Knowing this execution order helps avoid bugs when combining wrappers and observers in complex code.
Under the Hood
When you assign a new value to a property with observers, Swift's compiler generates extra code that calls willSet before changing the stored value and didSet after. The willSet observer receives the new value as a constant, allowing you to react before the change. After the value updates in memory, didSet runs with access to the old value. This happens automatically at runtime without extra calls from your code.
Why designed this way?
Swift designed property observers to provide a clean, declarative way to react to changes without forcing manual method calls everywhere. This keeps code concise and reduces errors. The separation into willSet and didSet gives flexibility to respond both before and after changes. Alternatives like manual setter methods were more verbose and error-prone, so observers improve safety and clarity.
┌───────────────┐
│ Assign newVal │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│   willSet     │  ← runs with newVal
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Update stored │
│   property    │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│    didSet     │  ← runs with oldVal
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does willSet let you change the new value before it is set? Commit to yes or no.
Common Belief:willSet lets you modify the new value before it is assigned to the property.
Tap to reveal reality
Reality:willSet provides the new value as a constant named newValue, but you cannot change it to affect the assignment. The property will still be set to the original new value.
Why it matters:Expecting to modify the new value in willSet leads to bugs where changes don't apply, causing confusion and incorrect program behavior.
Quick: Can you add willSet or didSet to computed properties? Commit to yes or no.
Common Belief:Property observers can be added to any property, including computed ones.
Tap to reveal reality
Reality:Observers only work on stored properties. Computed properties use getters and setters instead, and cannot have willSet or didSet.
Why it matters:Trying to add observers to computed properties causes compiler errors and wastes time debugging.
Quick: Does didSet run when a property is set to the same value it already has? Commit to yes or no.
Common Belief:didSet only runs when the property's value actually changes.
Tap to reveal reality
Reality:didSet runs every time the property is assigned, even if the new value is the same as the old one.
Why it matters:Assuming didSet only runs on real changes can cause unexpected repeated side effects or performance issues.
Quick: Can changing a property inside didSet cause infinite loops? Commit to yes or no.
Common Belief:You can safely modify a property inside its own didSet without issues.
Tap to reveal reality
Reality:Assigning to the same property inside didSet triggers didSet again, causing infinite recursion and crashes.
Why it matters:Not knowing this leads to runtime crashes that are hard to diagnose.
Expert Zone
1
Property observers do not run during initialization when setting default values, only on subsequent changes.
2
Observers on inherited properties in subclasses run when the property changes, allowing subclass-specific reactions.
3
Using observers with property wrappers requires understanding the order of execution to avoid subtle bugs.
When NOT to use
Avoid property observers when you need to validate or transform values before setting them; use computed properties with custom setters instead. Also, for complex reactive behavior, consider Combine or SwiftUI's @Published instead of observers.
Production Patterns
In real apps, property observers are used to update UI elements automatically when data changes, log changes for debugging, or trigger side effects like saving data. They help keep code reactive without heavy frameworks.
Connections
Reactive Programming
Property observers provide a simple form of reactive behavior by responding to data changes.
Understanding property observers helps grasp how reactive frameworks detect and respond to state changes automatically.
Event Listeners in UI Development
Both property observers and event listeners react to changes or actions, triggering code in response.
Knowing property observers clarifies how UI elements update dynamically when underlying data changes.
Observer Pattern (Software Design)
Property observers are a lightweight, built-in version of the observer pattern where properties notify code on changes.
Recognizing this connection helps understand broader design principles for decoupling and reactive systems.
Common Pitfalls
#1Trying to modify the new value inside willSet to change what gets assigned.
Wrong approach:var count: Int = 0 { willSet { newValue = newValue + 1 // error: newValue is a constant } }
Correct approach:var count: Int = 0 { willSet { print("New value will be \(newValue + 1)") } }
Root cause:Misunderstanding that newValue is a constant and cannot be changed inside willSet.
#2Adding observers to computed properties causing compiler errors.
Wrong approach:var fullName: String { willSet { print("Changing") } didSet { print("Changed") } get { return "John" } set { } }
Correct approach:var fullName: String { get { return "John" } set { /* custom setter code */ } } // No observers allowed here
Root cause:Confusing stored and computed properties and their capabilities.
#3Changing a property inside its own didSet without safeguards causing infinite loops.
Wrong approach:var value: Int = 0 { didSet { value += 1 } }
Correct approach:var value: Int = 0 { didSet { if value < 10 { value += 1 } } }
Root cause:Not realizing that assigning to the property inside didSet triggers didSet again.
Key Takeaways
Property observers let you run code automatically before and after a property's value changes, helping keep your code reactive and clean.
willSet provides the new value before assignment, and didSet provides the old value after assignment, but you cannot change the new value inside willSet.
Observers only work on stored properties, not computed ones, and run every time the property is assigned, even if the value doesn't change.
Modifying a property inside its own didSet can cause infinite loops unless carefully controlled.
Understanding how observers interact with property wrappers and inheritance helps write robust, maintainable Swift code.