Lazy property observers
Basics article available: PropertiesNew 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.