@Observable and didSet
When you mark a class as @Observable
you can no longer use didSet
because the macro converts it to a computed property, e.g. a simple:
var test: String = ""
When expanded becomes:
var test: String = ""
{
@storageRestrictions(initializes: _test)
init(initialValue) {
_test = initialValue
}
get {
access(keyPath: \.test)
return _test
}
set {
withMutation(keyPath: \.test) {
_test = newValue
}
}
_modify {
access(keyPath: \.test)
_$observationRegistrar.willSet(self, keyPath: \.test)
defer {
_$observationRegistrar.didSet(self, keyPath: \.test)
}
yield &_test
}
}
As you can see test
has become a computed property that is a wrapper around a private _test
property, a bit like how Objective C properties worked. As we know computed properties cannot have didSet
so if you write one for an observed property then that code is just ignored by the compiler, it should probably throw an error that this code will never be reached but it currently does it. One way I’ve come up with to implement didSet
is to move all the properties down a level, that is make your own public computed property test
that has the didSet logic; that reads and writes from _test
that is tracked and it generates a __test
(double underscore) for the storage, e.g.
private var _test: String = "" // this has the observation logic
public var test: String {
get {
_test // tracking reaches through test to _test
}
set {
_test = newValue
// do your didSet logic here
}
}
}
I also posted my workaround to Apple Developer Forums here.