Properties: Stored, Computed & Observers
Reviewed & published by Brayan K
Properties are how types expose their data. By the end of this lesson you'll tell stored from computed properties, write get/set, react to changes with willSet/didSet, and use lazy and static.
Part of the free Swift course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • Distinguish stored from computed properties
- • Write read-only and read/write computed properties with get/set
- • React to changes with willSet and didSet
- • Defer expensive setup with lazy
- • Share data on the type itself with static
1️⃣ Stored Properties
A stored property holds a real value as part of the instance. It can be a var (changeable) or let (fixed), and may have a default value.
// STORED properties hold an actual value as part of the instance.
struct Person {
var name: String // stored (variable)
let birthYear: Int // stored (constant)
var nickname = "—" // stored, with a default
}
var ada = Person(name: "Ada", birthYear: 1815)
print(ada.name) // Ada
ada.nickname = "Countess" // change a var stored property
print(ada.nickname) // Countess
print(ada.birthYear) // 1815 (a let — fixed)2️⃣ Computed Properties (get / set)
A computed property stores nothing — it calculates its value each time via a getter. Add a setter to make it read/write; the assigned value arrives as newValue.
// COMPUTED properties calculate their value each time — they store nothing.
struct Rectangle {
var width: Double
var height: Double
// Read-only computed property: just a getter (shorthand form).
var area: Double {
width * height
}
// Read/write computed property: get AND set.
var perimeterHalf: Double {
get { width + height }
set { width = newValue - height } // 'newValue' is the assigned value
}
}
var r = Rectangle(width: 3, height: 4)
print(r.area) // 12.0 (computed on access)
print(r.perimeterHalf) // 7.0
r.perimeterHalf = 10 // triggers the setter: width = 10 - 4 = 6
print(r.width) // 6.0
print(r.area) // 24.03️⃣ Property Observers (willSet / didSet)
Observers run code around a change to a stored property. willSet runs just before (with newValue); didSet runs just after (with oldValue).
// PROPERTY OBSERVERS run code when a stored property changes.
struct StepCounter {
var steps: Int = 0 {
willSet {
print("About to set steps to \(newValue)") // before the change
}
didSet {
print("Changed from \(oldValue) to \(steps)") // after the change
}
}
}
var counter = StepCounter()
counter.steps = 100
counter.steps = 250
// willSet gives you 'newValue'; didSet gives you 'oldValue'.4️⃣ lazy & static
lazy defers a stored property's initialization until first access (must be a var). static attaches a property to the type itself — one shared copy across all instances.
// LAZY: initialized only on first access (must be a var).
struct Report {
lazy var content: String = {
print("Building report...") // runs once, on first use
return "Big report data"
}()
}
var report = Report()
print("Created, not built yet")
print(report.content) // NOW it builds
print(report.content) // already built — no rebuild
// STATIC: belongs to the TYPE, shared across all instances.
struct Temperature {
static let boilingC = 100.0 // one shared constant on the type
var current: Double
}
print(Temperature.boilingC) // 100.0 (no instance needed)Your turn. Fill in the blanks, then run it and check the output.
// 🎯 YOUR TURN — replace each ___ then run it in SwiftFiddle.
struct Circle {
var radius: Double
// 1) A read-only COMPUTED property 'diameter' = radius * 2.
var diameter: Double {
___ // 👉 replace ___ with radius * 2
}
// 2) Observe changes to radius and announce the new value.
var label: String = "circle" {
___ { // 👉 replace ___ with didSet
print("Label is now \(label)")
}
}
}
var c = Circle(radius: 5)
print("Diameter: \(c.diameter)") // computed
c.label = "ring" // triggers the observer
// ✅ Expected output:
// Diameter: 10.0
// Label is now ringPro Tips
- 💡 Use computed properties for derived values (area, age, fullName) so they're never out of sync with their sources.
- 💡 Observers, not setters, for side effects on stored properties — they're cleaner than wrapping the property in methods.
- 💡 lazy for expensive setup that may not always run; it pays the cost only on first access.
- 💡 static for shared constants like Temperature.boilingC — no instance required.
Common Errors (and the fix)
- "'lazy' must not be used on a let" — lazy properties are set after init, so they must be var.
- Observers on a computed property don't compile — willSet/didSet are for stored properties. Put logic in the computed property's set instead.
- "Cannot assign to property: 'area' is a get-only property" — add a set, or treat it as read-only.
- Accessing a static via an instance — use the type name: Temperature.boilingC, not instance.boilingC.
📋 Quick Reference
| Kind | Code | Behaviour |
|---|---|---|
| Stored | var name: String | holds a value |
| Computed | var area: Double { ... } | calculates each time |
| Read/write | get { … } set { … } | uses newValue |
| Observer | didSet { … } | after a change |
| Lazy | lazy var x = ... | built on first use |
| Static | static let k = 1 | shared on the type |
Mini-Challenge: Thermostat
Build it, run it, and check your output against the example in the comments.
// 🎯 MINI-CHALLENGE: Thermostat
// 1. Define a struct 'Thermostat' with a stored var 'celsius' (Double, default 20).
// 2. Add a COMPUTED read-only property 'fahrenheit' = celsius * 9 / 5 + 32.
// 3. Add a didSet observer on 'celsius' that prints
// "Temp changed to C°" whenever it changes (use the new value).
// 4. Add a STATIC constant 'absoluteZeroC = -273.15'.
// 5. Create a var thermostat, print fahrenheit, set celsius = 25,
// print fahrenheit again, and print Thermostat.absoluteZeroC.
//
// ✅ Example output:
// 68.0
// Temp changed to 25.0
// 77.0
// -273.15
// your code here🎉 Lesson Complete!
- ✅ Stored properties hold values; computed properties calculate them each time
- ✅ Computed properties use get/set; setters receive newValue
- ✅ willSet/didSet observe changes to stored properties
- ✅ lazy defers setup; static shares data on the type
Practice quiz
What is a stored property?
- A value computed each time
- A method
- A constant or variable that stores a value as part of an instance
- A protocol
Answer: A constant or variable that stores a value as part of an instance. Stored properties hold an actual value as part of the instance.
What is a computed property?
- A property that calculates its value each time it's accessed
- A property that stores a value
- A static constant
- A method with no name
Answer: A property that calculates its value each time it's accessed. Computed properties run code to derive a value rather than store one.
Which clause is required for a read-only computed property?
- set only
- didSet
- willSet
- get (the getter)
Answer: get (the getter). A computed property needs at least a getter to return its value.
When does a `didSet` observer run?
- Before the value changes
- Just after the property's value is set
- Only at init
- Never
Answer: Just after the property's value is set. didSet runs immediately after the new value has been stored.
When does a `willSet` observer run?
- Just before the property is set to a new value
- After the value changes
- Only on read
- At deinit
Answer: Just before the property is set to a new value. willSet runs right before the new value is assigned.
Inside willSet, what is the default name for the incoming value?
- oldValue
- value
- newValue
- next
Answer: newValue. willSet provides newValue by default; didSet provides oldValue.
What does a `lazy` stored property do?
- Loads at app launch
- Is only computed/initialized on first access
- Cannot be changed
- Runs on a background thread
Answer: Is only computed/initialized on first access. A lazy property defers its initialization until first accessed.
Can a lazy property be a constant declared with `let`?
- Yes
- Only in classes
- Only with get/set
- No — lazy must be a var
Answer: No — lazy must be a var. lazy properties must be var because their value is set after init, on first use.
What does the `static` keyword create on a type?
- An instance property
- A type-level property shared across all instances
- A computed getter
- A deinitializer
Answer: A type-level property shared across all instances. static makes a property (or method) belong to the type itself, not instances.
Do computed properties store a value in memory?
- Yes, like stored properties
- Only the first time
- No — they compute on each access
- Only if lazy
Answer: No — they compute on each access. Computed properties hold no storage; they recalculate each time.
Continue this course
- Previous: Classes & Reference Types
- Next: Methods & Initializers — Instance and type methods, designated vs convenience initializers, self
Frequently asked questions
What is the difference between a stored and a computed property?
A stored property holds an actual value in memory as part of each instance — a name, a count, a flag. A computed property stores nothing; instead it runs a getter to calculate its value every time you access it, often deriving it from other properties (like area from width and height). Use stored for raw data, computed for values that are always derived.
What do willSet and didSet do?
They are property observers that run code around a change to a stored property. willSet runs just before the new value is assigned and exposes it as newValue; didSet runs just after, exposing the previous value as oldValue. They're ideal for reacting to changes — updating a UI, logging, validating, or keeping related values in sync — without wrapping the property in extra methods.
When and why would I use a lazy property?
Mark a stored property lazy when its initial value is expensive to produce and might not always be needed. A lazy property isn't initialized until the first time you access it, so you avoid paying that cost up front. Lazy properties must be declared with var, because their value is established after the instance is initialized, on first use.
What does the static keyword do?
static creates a type property (or method) that belongs to the type itself rather than to any instance — there's a single shared copy. You access it on the type, like Temperature.boilingC, without creating an instance. It's perfect for constants, shared configuration, and factory helpers that are conceptually about the type as a whole.
Can a computed property have a setter?
Yes. A computed property with both get and set is read/write: the getter returns a derived value and the setter runs code when you assign to it, typically updating one or more stored properties. Inside the setter, the assigned value is available as newValue (or a name you specify). A computed property with only a getter is read-only.