KVO on Swift's computed properties
That code won't work for two reasons:
You must add the
dynamic
attribute to thearea
property, as described in the section “Key-Value Observing” under “Adopting Cocoa Design Patterns” in Using Swift with Cocoa and Objective-C.You must declare that
area
depends onwidth
andheight
as described in “Registering Dependent Keys” in the Key-Value Observing Programming Guide. (This applies to Objective-C and Swift.) And for this to work, you also have to adddynamic
towidth
andheight
.(You could instead call
willChangeValueForKey
anddidChangeValueForKey
wheneverwidth
orheight
changes, but it's usually easier to just implementkeyPathsForValuesAffectingArea
.)
Thus:
import Foundation
class MyObject: NSObject {
@objc dynamic var width: Double = 0
@objc dynamic var height: Double = 0
@objc dynamic private var area: Double {
return width * height
}
@objc class func keyPathsForValuesAffectingArea() -> Set<String> {
return [ "width", "height" ]
}
func register() {
self.addObserver(self, forKeyPath: "area", options: [ .old, .new ], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("observed \(keyPath) \(change)")
}
}
let object = MyObject()
object.register()
object.width = 20
object.height = 5
Output:
observed Optional("area") Optional([__C.NSKeyValueChangeKey(_rawValue: new): 0, __C.NSKeyValueChangeKey(_rawValue: kind): 1, __C.NSKeyValueChangeKey(_rawValue: old): 0])
observed Optional("area") Optional([__C.NSKeyValueChangeKey(_rawValue: new): 100, __C.NSKeyValueChangeKey(_rawValue: kind): 1, __C.NSKeyValueChangeKey(_rawValue: old): 0])
As @Rob stated in his answer, make area
dynamic
to be observed from objective-c
Now add willSet { }
and didSet { }
for width
and height
properties,
inside willSet
for both properties add this self.willChangeValueForKey("area")
and in didSet
add self.didChangeValueForKey("area");
Now observers of area
will be notified every time width or height change.
Note: this code is not tested, but I think it should do what expected