Software, Tech & Coding simplified.

didSet and willSet in Swift

In Swift, didSet and willSet methods act as property observers.

  • willSet runs a piece of code right before a property changes.
  • didSet runs a piece of code right after the property has changed.

For example:

var name: String? {
    willSet { print("Name will change now.") }
    didSet { print("Name was changed.")}
}

name = "Jack"

Output:

Name will change now.
Name was changed.

Here assigning “Jack” to name triggered both willSet and didSet blocks.

In this article, we are going to take a deeper dive into property observers in Swift.

In essence, you are going to learn:

  • How and when to create property observers.
  • What are the limitations.
  • Some real-life use case ideas.

Property Observers in Swift

In Swift, you can attach property observers to variables to run code when the variable changes. These property observers are called willSet and didSet. The former runs right before the property changes, and the latter runs right after the changes were made.

But why is this useful?

It is all about convenience.

Of course, you could use a regular function to run a piece of code when updating a value.

For example:

var name: String?

print("Name will change now.")
name = "Jack"
print("Name was changed.")

Output:

Name will change now.
Name was changed.

But do you really remember to do this every time you update the name?

Chances are you do not.

This is where property observers come in handy.

Property observers are blocks of code that are assigned to a variable. These blocks of code are guaranteed to run whenever you update the variable. Thus you do not have to pay attention to it yourself.

For example:

var name: String? {
    willSet { print("Name will change now.") }
    didSet { print("Name was changed.")}
}

name = "Jack"

Output:

Name will change now.
Name was changed.

No matter how you change the name, the property observers willSet and didSet will run.

Now you have a basic understanding of property observers in Swift.

Creating Property Observers in Swift

You can create a property observer by opening a set of curly braces after the variable name and defining the willSet and didSet blocks in it.

var property: Type = someValue {
    willSet {}
    didSet {}
}

Let’s take a closer look at creating property observers starting with the limitations.

Limitations

There are some restrictions to property observers you need to be aware of.

  1. A property observer can only be used with a variable (created with var). This is because a let constant cannot be modified. Thus it would be meaningless to track changes of something that cannot be changed.
  2. A property has to have an initial value, such as nil.
  3. You cannot use property observers with computed properties. This is because a computed property does not store a value.
  4. You cannot attach property observers to lazy variables. Lazy variables are initialized when they are called for the first time. A lazy variable does not have an initial value which contradicts the first restriction.

Next, let’s take a look at how you can pass parameters to property observers.

Parameters

Property observers also take parameters that refer to the old and the new values.

By default, these parameters are called oldValue and newValue.

  • The willSet block always stores the incoming value as newValue.
  • The didSet block always stores the previous value as oldValue.

To access these parameters, you do not need to declare them anywhere. They are automatically in your use.

For example:

var name: String = "Alice" {
    willSet { print("Name will from \(name) to \(newValue)") }
    didSet { print("Name changed from \(oldValue) to \(name)")}
}

name = "Jack"

Output:

Name will from Alice to Jack
Name changed from Alice to Jack

As you can see from the code, the newValue, and oldValue automatically referenced the name before and after the change.

Now you know how the default parameters work with property observers.

It is also possible not to use the default namings but to use custom parameter names instead.

This happens by specifying the parameter name right after the property observer block before the curly braces:

var property: Type = someValue {
    willSet(previous) {}
    didSet(updated) {}
}

For instance, let’s repeat the name example by accessing the name with newName and oldName:

var name: String = "Alice" {
    willSet(newName) {
        print("Name will from \(name) to \(newName)")
    }
    didSet(oldName) {
        print("Name changed from \(oldName) to \(name)")
    }
}

name = "Jack"

Output:

Name will from Alice to Jack
Name changed from Alice to Jack

Optionals

As you remember from the restrictions of property observers, you can only observe properties that have an initial value. This initial value can be nil, though.

So if you want to declare a variable with property observers, you need to either:

  • Assign it some initial value.
  • Make it an optional (then the initial value will be nil automatically).

For example, this property with observers causes an error:

var name: String {
    willSet(newName) {
        print("Name will become \(newName)")
    }
    didSet {
        print("Name changed to \(name)")
    }
}

Output:

main.swift:6:5: error: non-member observing properties require an initializer
var name: String {

This happens because we try to initialize the name variable without a value.

To create a property with property observers without specifying an initial value, make the property optional.

This way it will be assigned nil automatically.

When dealing with optional values, remember to handle the values by unwrapping the optional first.

For example, let’s fix the above property by making it optional:

var name: String? {
    willSet(newName) {
        // Optional handling
        if let newName = newName {
            print("Name will become \(newName)")
        }
    }
    didSet {
        // Optional handling
        if let name = name {
            print("Name changed to \(name)")
        }
    }
}

Now the name is initialized at nil. Also, notice how we do some optional handling inside the property observers to make sure we are not reading nil.

You Do Not Need Both willSet and didSet

You do not need to specify both willSet and didSet property observers. You can leave the other out depending on what you are building.

For instance, if you only care about what happens right after a property gets updated, you can only specify the didSet block into the property:

var name: String = "Alice" {
    didSet {
        print("Name changed to \(name)")
    }
}

name = "Jack"

Output:

Name changed to Jack

Now you have a good understanding of what property observers are in Swift and how you can use them.

Let’s finally go through some example use cases.

Example Uses for Property Observers in Swift

You can use property observers to perform an action when a variable changes.

Let’s see a couple of examples of how you could benefit from using them in your application.

Username Validation

One example of how you could benefit from property observers is by running an automatic input validation as soon as a property changes.

For instance, let’s validate a username such that if the name is invalid, the previous username is used:

func isValid(_ str: String) -> Bool {
    return !str.contains("ö")
}

var username: String = "Alice" {
    didSet(oldName){
        if !isValid(username) {
            username = oldName
            print("Invalid character. Setting name back to \(oldName)")
        } else {
            print("The new username is \(username)")
        }
    }
}

username = "Bob" // success
username = "Böb" // fail

Output:

The new username is Bob
Invalid character. Setting name back to Bob

Game Score Updates

Imagine you have a game application that keeps track of the player’s score.

Each time the score updates, the UI should reflect that by updating the score label.

It would be a perfectly valid approach to separately call a function to render the updated score into the view.

But what could be a better solution is to make it possible to automatically render the updated score whenever it gets updated.

This is possible by using property observers, namely, the didSet block.

Here is a code skeleton of what it might look like:

var score: Int = 0 {
    didSet {
        display(score)
    }
}

score += 10

This wraps it up for property observers in Swift.

Conclusion

Today you learned what the didSet and willSet methods do in Swift.

To recap, the didSet and willSet blocks are called property observers.

Property observers observe and react to changes in variables.

  • The willSet observer runs when the variable is about to update.
  • The didSet runs when the variable has been updated.

Property observers can become handy if you have to take action right before or after updating a property.

Thanks for reading.

Happy coding!

Further Reading

50 Swift Interview Questions

Share

Share on twitter
Share on linkedin
Share on facebook
Share on pinterest
Share on email