Swift Properties

A property is a named value that belongs to a struct, class, or enum. Properties describe what an object knows about itself. A student has a name, a car has a color, and a bank account has a balance. Swift offers several kinds of properties, and each kind solves a different problem.

Consider a house. The furniture inside is stored data. The total floor area comes from a calculation using length and width. A doorbell reacts whenever someone arrives. Swift maps these three ideas to stored properties, computed properties, and property observers.

Diagram: Kinds of Properties

                  Properties
                      |
     +----------------+------------------+
     |                |                  |
  Stored           Computed            Type
 (keeps a value)  (calculates one)   (belongs to the type)
     |
  +--+-----------+
  |              |
 lazy        observers
 (built      (willSet,
 on demand)   didSet)

Stored Properties

A stored property keeps a value in memory. Declare it with let for a fixed value or var for a changeable value.

struct Book {
    let title: String
    var pagesRead: Int = 0
}

var novel = Book(title: "Sky Road")
novel.pagesRead = 120
print("\(novel.title): \(novel.pagesRead) pages")
// Output: Sky Road: 120 pages

Computed Properties

A computed property stores nothing. It runs a small calculation every time you read it. A computed property accepts a new value through a setter.

struct Rectangle {
    var width: Double
    var height: Double

    var area: Double {
        width * height
    }
}

let room = Rectangle(width: 4, height: 5)
print(room.area)   // Output: 20.0

The area property always matches the current width and height. No stale data can appear.

Getters and Setters

struct Temperature {
    var celsius: Double

    var fahrenheit: Double {
        get {
            celsius * 9 / 5 + 32
        }
        set {
            celsius = (newValue - 32) * 5 / 9
        }
    }
}

var today = Temperature(celsius: 100)
print(today.fahrenheit)    // Output: 212.0

today.fahrenheit = 32
print(today.celsius)       // Output: 0.0

The setter receives the incoming value under the automatic name newValue. Both properties stay in sync because only celsius holds real data.

Property Observers

Observers watch a stored property and run code when its value changes. The willSet block runs before the change, and the didSet block runs after it.

class StepTracker {
    var steps = 0 {
        willSet {
            print("About to set steps to \(newValue)")
        }
        didSet {
            print("Added \(steps - oldValue) steps")
        }
    }
}

let tracker = StepTracker()
tracker.steps = 500
// Output:
// About to set steps to 500
// Added 500 steps

Observers suit tasks such as refreshing a screen label or saving a score after every change.

Lazy Properties

A lazy property delays its creation until code reads it for the initial time. Heavy work that might never be needed benefits from this trick.

class Report {
    lazy var summary: String = {
        print("Building summary...")
        return "Sales grew by 8 percent"
    }()
}

let report = Report()
print("Report created")
print(report.summary)
// Output:
// Report created
// Building summary...
// Sales grew by 8 percent

Mark a lazy property with var, because its value arrives after the object exists.

Type Properties

A type property belongs to the type itself and not to one instance. Every instance shares it. Declare it with the static keyword.

struct Circle {
    static let pi = 3.14159
    var radius: Double

    var area: Double {
        Circle.pi * radius * radius
    }
}

print(Circle.pi)                    // Output: 3.14159
print(Circle(radius: 2).area)       // Output: 12.56636

Choosing the Right Kind

Pick a stored property for data that needs a place in memory. Pick a computed property for a value derived from other properties. A computed property never goes out of date, and a stored copy of the same value can.

Property observers fit side effects such as updating a label or saving a score. Avoid heavy work inside didSet, because the code runs on every change.

Common Mistakes

Reading the property inside its own getter creates an endless loop. A computed property must read other properties only.

Observers do not run during initialization of the same type. Assign the value after the instance exists when you need the observer to fire.

Real-World Use

Fitness apps store steps and compute the daily goal percentage. Weather apps store Celsius and compute Fahrenheit. Forms use observers to enable a submit button the moment every field holds valid text.

Properties in Enums and Classes

Structs, classes, and enums all support computed properties and type properties. Enums cannot hold stored instance properties, but each case can carry associated values and expose computed properties that describe the case. Classes support every kind of property, and observers on a class property fire when a subclass changes the value.

Constants declared with let inside a struct lock the value after initialization. A constant struct instance locks every property, including those declared with var. A constant class instance locks only the reference. The properties inside remain changeable.

Key Takeaways

  • Stored properties keep values, and computed properties calculate them.
  • Observers react to changes with willSet and didSet.
  • Lazy properties build their value on first use.
  • Type properties belong to the type and use the static keyword.