Weekly Swift articles, podcasts and tips by John Sundell.

Lazy property observers

Published on 06 Oct 2020
Basics article available: Properties

New in Swift 5.3: Property observers can now be attached to lazy properties, which makes it possible for us to automatically observe when a new value was assigned to a given property, even if its value is lazily loaded when first accessed.

As an example, let’s say that an app that we’re working on supports multiple themes, and that we’ve implemented a ThemeController that’s responsible for keeping track of which theme that the user has currently selected, as well as for loading and syncing such Theme values to disk using Foundation’s UserDefaults API.

To make sure that the latest value is always persisted, we’ve added a didSet property observer to our controller’s theme property, but before Swift 5.3, we couldn’t then also make that property lazy — meaning that we’d first have to give it a default value when declaring it, and then load its actual underlying value as part of our controller’s initializer, like this:

class ThemeController {
    var theme = Theme.systemDefault {
        didSet { saveTheme() }
    }

    private let defaults: UserDefaults
    private let defaultsKey = "theme"

    init(defaults: UserDefaults = .standard) {
        self.defaults = defaults
        theme = loadTheme()
    }

    private func loadTheme() -> Theme {
        let savedTheme = defaults
            .string(forKey: defaultsKey)
            .flatMap(Theme.init)

        return savedTheme ?? .systemDefault
    }

    private func saveTheme() {
        defaults.setValue(theme.rawValue, forKey: defaultsKey)
    }
}

But now, when using Swift 5.3, we can make that property load its value lazily when it’s first accessed, which lets us call our loadTheme method directly from within its declaration — giving us a simpler (and possibly slightly faster) implementation:

class ThemeController {
    lazy var theme = loadTheme() {
        didSet { saveTheme() }
    }

    ...

    init(defaults: UserDefaults = .standard) {
        self.defaults = defaults
    }

    ...
}

To learn more about lazy properties in general, check out “Using lazy properties in Swift”.

The above is definitely not a ground-breaking feature that will forever change the way we write Swift code, but it’s another one of those small but really nice improvements that continue to refine how Swift’s various language features can be combined.

Support Swift by Sundell by checking out this sponsor:

Architecting SwiftUI apps with MVC and MVVM
Architecting SwiftUI apps with MVC and MVVM

Architecting SwiftUI apps with MVC and MVVM: Although you can create an app simply by throwing some code together, without best practices and a robust architecture, you’ll soon end up with unmanageable spaghetti code. Learn how to create solid and maintainable apps with fewer bugs using this free guide.