Trigger property observers from initializers in Swift

In Swift, property observers such as willSet and didSet are not called when a property is set in an initializer. This is by design, as the initializer's purpose is to set up the initial state of an object, and during this phase, the object is not yet fully initialized. However, if we need to perform some actions similar to what we'd do in property observers during initialization, there are some workarounds.

# Set properties after initialization

One approach is to set the properties after the object has been initialized.

class MyClass {
    var myProperty: String {
        willSet {
            print("Will set myProperty to \(newValue)")
        }
        didSet {
            print("Did set myProperty to \(myProperty), previously \(oldValue)")
        }
    }

    init() {
        myProperty = "Initial value"
    }
}

let myObject = MyClass()
myObject.myProperty = "New value"

In this example, the property observers will not trigger during the initial assignment in the initializer but will trigger on subsequent property changes.

# Separate property setup method

Another approach is to use a separate method to set up the property.

class MyClass {
    var myProperty: String {
        willSet {
            print("Will set myProperty to \(newValue)")
        }
        didSet {
            print("Did set myProperty to \(myProperty), previously \(oldValue)")
        }
    }
    
    init(value: String) {
        myProperty = "Initial value"
        setupPropertyValue(value: value)
    }
    
    private func setupPropertyValue(value: String) {
        myProperty = value
    }
}

let myObject = MyClass(value: "New value")

This approach ensures that the property observers are triggered during the setup phase after the initialization of the object is completed.

Integrating SwiftUI into UIKit Apps by Natalia Panferova book coverIntegrating SwiftUI into UIKit Apps by Natalia Panferova book cover

Enhance older apps with SwiftUI!$45

A detailed guide on gradually adopting SwiftUI in UIKit projects

Updated for iOS 18 and Xcode 16!

Integrating SwiftUI into UIKit Appsby Natalia Panferova

  • Upgrade your apps with new features like Swift Charts and Widgets
  • Support older iOS versions with effective backward-compatible strategies
  • Seamlessly bridge state and data between UIKit and SwiftUI using the latest APIs

Enhance older apps with SwiftUI!

A detailed guide on gradually adopting SwiftUI in UIKit projects

Integrating SwiftUI into UIKit Apps by Natalia Panferova book coverIntegrating SwiftUI into UIKit Apps by Natalia Panferova book cover

Integrating SwiftUI
into UIKit Apps

by Natalia Panferova

Updated for iOS 18 and Xcode 16!

$45

# Create a defer closure

An alternative approach involves using a defer block within the initializer.

class MyClass {
    var myProperty: String {
        willSet {
            print("Will set myProperty to \(newValue)")
        }
        didSet {
            print("Did set myProperty to \(myProperty), previously \(oldValue)")
        }
    }

    init(value: String) {
        defer { myProperty = value } 
        myProperty = "Initial value"
    }
}

let myObject = MyClass(value: "New value")

The defer block ensures that the didSet logic is called after the initial value is set.

# Manually trigger side effects

If we need to perform some specific actions during initialization, we can also manually call the same methods or code blocks that we would have called in our observers.

class MyClass {
    var myProperty: String {
        willSet {
            propertyWillChange(newValue)
        }
        didSet {
            propertyDidChange(oldValue)
        }
    }

    init(value: String) {
        myProperty = value
        propertyDidChange(nil)
    }

    private func propertyWillChange(_ newValue: String) {
        print("Will set myProperty to \(newValue)")
    }

    private func propertyDidChange(_ oldValue: String?) {
        print("Did set myProperty to \(myProperty), previously \(oldValue)")
    }
}

let myObject = MyClass(value: "New value")

In this example, the propertyDidChange() method is manually called within the initializer to simulate the didSet observer.


While property observers don't fire during initialization, these workarounds offer alternative ways to achieve similar behavior. We choose the approach that best fits our use case and coding style.

As someone who has worked extensively with Swift, I've gathered many insights over the years. I've compiled them in my book Swift Gems, which is packed with advanced tips and techniques to help intermediate and advanced Swift developers enhance their coding skills. From optimizing collections and handling strings to mastering asynchronous programming and debugging, "Swift Gems" provides practical advice to elevate your Swift development. Grab your copy of Swift Gems and let's explore the advanced techniques together.

Swift Gems by Natalia Panferova book coverSwift Gems by Natalia Panferova book cover

Level up your Swift skills!$35

100+ tips to take your Swift code to the next level

Swift Gemsby Natalia Panferova

  • Advanced Swift techniques for experienced developers bypassing basic tutorials
  • Curated, actionable tips ready for immediate integration into any Swift project
  • Strategies to improve code quality, structure, and performance across all platforms

Level up your Swift skills!

100+ tips to take your Swift code to the next level

Swift Gems by Natalia Panferova book coverSwift Gems by Natalia Panferova book cover

Swift Gems

by Natalia Panferova

$35