tezvyn:

Swift Properties: Accessors in Disguise

AI-drafted, machine-checkedintermediate

A Swift property runs code on every read or write. Use stored properties for state, computed properties for derived values, and observers for side effects. The footgun is heavy work in a computed getter, turning innocent dot syntax into a performance trap.

WHY IT EXISTS: Before Swift, Objective-C separated instance variables from accessor methods. You declared an ivar, then wrote manual getters and setters to enforce invariants, trigger KVO, or compute derived values. That separation leaked implementation details and invited boilerplate. Swift properties collapse storage and access into a single linguistic unit so that the call site always uses dot syntax, while the implementation freely chooses between storage, computation, or observation without breaking the caller.

THE MENTAL MODEL: Think of a property as a smart mailbox. To the outside world it looks like a simple slot that holds a letter. Inside, it might be a direct storage bin, a photocopier that generates the letter on demand, or an alarm that rings every time a letter is dropped in. The consumer never knows which mechanism is behind the slot, which is exactly the point: properties decouple the interface of state from the mechanics of state.

HOW IT WORKS: A stored property reserves memory inside the instance and optionally runs willSet and didSet observers when the value changes. A computed property does not reserve storage; instead it executes a getter block to produce a value and an optional setter block to project changes back to other stored properties. A lazy stored property postpones initialization until first access, running a closure once and caching the result. A property wrapper wraps the underlying storage and access logic in a reusable type, letting you annotate declarations with behavior like atomicity, validation, or dependency injection.

WHEN TO USE IT: Use stored properties for the raw data that defines an objects identity or current condition. Use computed properties when a value is derived from other properties and must stay in sync without manual update calls. Use property observers when you need side effects such as logging, cache invalidation, or view layout triggers every time a value mutates. Use property wrappers when the same access logic repeats across many declarations, such as publishing changes to SwiftUI or clamping numeric ranges.

WHEN NOT TO USE IT: Do not use a computed property for expensive or asynchronous work. If a getter triggers a network request, database query, or long-running algorithm, dot syntax hides the cost and callers will treat it like a free field access. Do not use observers for cross-object coordination; they run synchronously during mutation and can create confusing re-entrancy or cascading updates that are hard to debug. Do not use lazy properties if the initialization closure depends on external state that might change before first access.

ONE CANONICAL EXAMPLE: A view controller that tracks a download progress bar might store the raw byte count as a stored property. It exposes a computed progress property that divides bytesReceived by totalBytes, keeping the UI label in sync without a manual refresh method. It attaches a didSet observer to progress so that whenever the value crosses one hundred percent it automatically hides the progress view. If many view controllers need the same clamping behavior, a property wrapper can enforce that progress stays between zero and one across the entire codebase.

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.