---
title: "Property observers already give you the old and new value"
description: "willSet and didSet take a parameter, and both have a default name — so naming them explicitly is usually redundant. The rules about when they do not fire matter more."
author: "Omar Albeik"
date: 2019-12-06
type: note
topics: [software, engineering]
language: en
reading_time_minutes: 2
canonical_url: https://omaralbeik.com/en/blog/use-property-observers
translation_url: https://omaralbeik.com/ar/blog/use-property-observers
source_url: https://omaralbeik.com/en/blog/use-property-observers.md
---

# Property observers already give you the old and new value

`willSet` and `didSet` each receive the value they are missing: `willSet` gets
the incoming one, `didSet` gets the previous one. Both have default names, so
this compiles as written:

```swift
var language = "ObjC" {
  willSet {
    print("about to replace \(language) with \(newValue)")
  }
  didSet {
    print("replaced \(oldValue) with \(language)")
  }
}
```

Each observer reads one value from the parameter and the other from the
property. In `willSet` the property still holds the old value; in `didSet` it
already holds the new one.

The explicit form — `willSet(newValue)`, `didSet(oldValue)` — names the
parameter exactly what it is called by default, so it adds nothing. It is worth
writing only when a different name reads better:

```swift
didSet(previousUser) {
  guard previousUser.id != user.id else { return }
  reload()
}
```

When they do not run causes more confusion than what the parameters are called.
There are three rules.

## They do not run during initialization

Assigning to a property from within `init` does not call its observers,
including through the memberwise initializer. Setup that belongs in `didSet`
has to be called explicitly at the end of `init`, or it silently does not
happen for the first value.

## They do not run on assignment from inside `didSet`

Writing to the property in its own `didSet` does not re-enter the observer,
which is what stops it recursing. So a `didSet` that clamps or normalises its
own value works, and one written expecting to see its own write come back
around does not.

## They run on every write, not every change

Setting a property to the value it already holds still fires both observers. If
the body triggers a reload or a layout pass, an equality check is usually what
was meant:

```swift
didSet {
  guard oldValue != items else { return }
  tableView.reloadData()
}
```

The same applies one level down. If the property holds a struct, mutating one
of its fields is a write to the whole property, so `settings.fontSize = 14`
fires `didSet` on `settings` — worth knowing before putting expensive work in
one.
