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

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.0

3️⃣ 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 ring

Pro Tips

Common Errors (and the fix)

📋 Quick Reference

KindCodeBehaviour
Storedvar name: Stringholds a value
Computedvar area: Double { ... }calculates each time
Read/writeget { … } set { … }uses newValue
ObserverdidSet { … }after a change
Lazylazy var x = ...built on first use
Staticstatic let k = 1shared 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!

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

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.

Related lessons