SWIFT

Swift Property Observers: Syntax, Use Cases, and Best Practices

Swift property observers let you monitor and respond to changes in property values. These observers run custom code before or after a property’s value is modified. You typically use property observers in Swift to trigger actions like updating UI, logging changes, validating data, or synchronizing state across objects.

Unlike computed properties, property observers apply to stored properties—those that hold actual values—and are executed during assignment, even if the new value is the same as the old one.


Syntax of Property Observers in Swift

There are two types of property observers Swift supports:

  • willSet: Executes just before the value is set. It provides access to the new value.
  • didSet: Executes right after the value is set. It provides access to the previous value.

Here's an example of both:

var score: Int = 0 {
    willSet {
        print("Score will change from \(score) to \(newValue)")
    }
    didSet {
        print("Score changed from \(oldValue) to \(score)")
    }
}

Assigning a new value will trigger both observers:

score = 10
// Output:
// Score will change from 0 to 10
// Score changed from 0 to 10

You can omit one of the two if it's not needed.


The newValue and oldValue Keywords

In the willSet block, Swift provides a newValue variable, which represents the value that’s about to be assigned. You can rename it for clarity:

willSet(newScore) {
    print("Preparing to set new score: \(newScore)")
}

In the didSet block, oldValue refers to the property’s value before it was changed. These implicit variables allow you to compare, react, and perform differential updates.


Applying Observers to Stored Properties

You can use Swift property observers with stored properties in both classes and structs:

struct Temperature {
    var celsius: Double {
        didSet {
            print("Celsius updated to \(celsius)")
        }
    }
}

Each time celsius changes, the didSet block is called. This is especially useful when state changes need to ripple through related components.


Use Cases for Property Observers in Swift

Property observers Swift developers use serve many practical purposes:

UI Updates

You can update a UI element in response to data changes.

var username: String = "" {
    didSet {
        nameLabel.text = username
    }
}

Logging Changes

var loggedIn: Bool = false {
    didSet {
        print("User login state changed: \(loggedIn)")
    }
}

Input Validation

var quantity: Int = 1 {
    didSet {
        if quantity < 1 {
            quantity = 1
        }
    }
}

In this case, setting an invalid value automatically corrects it.

State Synchronization

Observers help keep two related properties in sync:

var userID: Int = 0 {
    didSet {
        fetchUserDetails(for: userID)
    }
}

Property Observers with Optionals

You can use property observers with optional properties, and they will still be triggered when the value changes:

var token: String? {
    didSet {
        print("Auth token updated.")
    }
}

Setting it to nil or assigning a new token triggers the observer.


Observers in Class Inheritance

Observers don’t get inherited by subclasses. If you subclass a class with property observers, the subclass must explicitly redefine the property to observe changes.

class Base {
    var name: String = "" {
        didSet {
            print("Base updated name")
        }
    }
}

class Child: Base {
    // If you want the observer here, you must override it
    override var name: String {
        didSet {
            print("Child updated name")
        }
    }
}

Without the override, the Child class won't inherit the observer.


Property Observers and Initializers

Observers are not called during property initialization in the same scope. That means when a property is set as part of the object’s own initialization, observers don’t run.

var message: String = "Hello" {
    didSet {
        print("Message changed")
    }
}

init() {
    message = "Welcome" // didSet is NOT triggered here
}

However, if a superclass sets a property on a subclass with observers, those observers do get triggered.


Observers with Static Properties

You can’t add observers to static (type-level) stored properties directly. Swift only allows observers on instance properties.

static var count: Int = 0 // ❌ Can't observe this

As a workaround, use computed properties with manual logic if you need change tracking for static values.


Differences Between Property Observers and Computed Properties

It’s important to understand how Swift property observers differ from computed properties:

  • A computed property recalculates its value every time you access it and does not store data.
  • A stored property with observers keeps its value in memory and runs additional logic on assignment.

Example:

// Computed property
var isAdult: Bool {
    return age >= 18
}

// Stored property with observers
var age: Int = 0 {
    didSet {
        print("Age updated")
    }
}

Use property observers when you want to monitor actual data changes; use computed properties when the value is derived.


Nesting Observers in Property Wrappers

You can combine property observers Swift patterns with property wrappers like @Published or @State in SwiftUI. However, once wrapped, you can't use willSet or didSet directly on the property. Instead, you must observe changes in other ways, like via onChange or Combine publishers.


Avoiding Common Pitfalls

Some best practices when using Swift property observers:

  1. Avoid infinite loops: If didSet modifies the same property again, you may trigger a loop.
  2. Keep logic lightweight: Observers should not contain complex operations like heavy computations or I/O.
  3. Don’t rely on initialization triggers: Observers won’t fire during direct property initialization inside init().
  4. Use observers for side effects, not for value computation: Keep logic deterministic and side-effect focused.

Summary

Swift property observers provide a robust way to react to changes in stored property values. By using willSet and didSet, you can update UI elements, log state changes, validate data, or trigger synchronization across your app. These observers are simple to implement, highly readable, and offer fine-grained control over property behavior.

Knowing when to use property observers Swift developers often face helps strike the right balance between reactive and clean code. They are a vital tool in many common programming patterns, especially in SwiftUI, where state-driven updates are central to UI behavior.

Learn to Code in Swift for Free
Start learning now
button icon
To advance beyond this tutorial and learn Swift by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

You can code, too.

© 2025 Mimo GmbH

Reach your coding goals faster