Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KVO on Swift's computed properties

Is KVO on a computed property possible in Swift?

var width = 0
var height = 0

private var area : Double {
    get {
        return with * height
    }
}

self.addOberser(self, forKeyPath: "area", ......

Would a client code modifying the with or height trigger observeValueForKeyPath?

Just checking before engaging on a mayor class refactor. KVO's syntax being as annoying as it's is not worth even a playground if someone has an answer at hand. (I am assuming the answer is NO)

like image 387
David Homes Avatar asked Apr 11 '16 17:04

David Homes


1 Answers

That code won't work for two reasons:

  1. You must add the dynamic attribute to the area property, as described in the section “Key-Value Observing” under “Adopting Cocoa Design Patterns” in Using Swift with Cocoa and Objective-C.

  2. You must declare that area depends on width and height 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 add dynamic to width and height.

    (You could instead call willChangeValueForKey and didChangeValueForKey whenever width or height changes, but it's usually easier to just implement keyPathsForValuesAffectingArea.)

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])
like image 186
rob mayoff Avatar answered Sep 28 '22 05:09

rob mayoff