Last active
November 3, 2016 13:52
-
-
Save lorentey/30c18028316afa48c1b3820a6109a414 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
@objc class Person: NSObject { | |
var _firstName: String = "John" | |
var _lastName: String = "Smith" | |
dynamic var firstName: String { get { return _firstName } set { _firstName = newValue } } | |
dynamic var lastName: String { get { return _lastName } set { _lastName = newValue } } | |
dynamic var name: String { return "\(firstName) \(lastName)" } | |
class func keyPathsForValuesAffectingName() -> NSSet { return ["firstName", "lastName"] } | |
} | |
class Observer: NSObject { | |
let object: AnyObject | |
let keyPath: String | |
var context: UInt8 = 0 | |
init(object: AnyObject, keyPath: String) { | |
self.object = object | |
self.keyPath = keyPath | |
super.init() | |
object.addObserver(self, forKeyPath: keyPath, options: [.prior, .old, .new], context: &context) | |
} | |
deinit { | |
object.removeObserver(self, forKeyPath: keyPath, context: &context) | |
} | |
override func observeValue(forKeyPath keyPath: String?, of object: Any?, | |
change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { | |
if context == &self.context { | |
guard let change = change else { return } | |
let old = change[.oldKey] == nil || change[.oldKey] as? NSNull != nil ? "nil" : "\(change[.oldKey]!)" | |
let new = change[.newKey] == nil || change[.newKey] as? NSNull != nil ? "nil" : "\(change[.newKey]!)" | |
if change[.notificationIsPriorKey] as? Bool == true { | |
print("willChange(old: \(old))") | |
} | |
else { | |
print("didChange(old: \(old), new: \(new))") | |
} | |
} | |
else { | |
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) | |
} | |
} | |
} | |
let person = Person() | |
let name = Observer(object: person, keyPath: "name") | |
person.willChangeValue(forKey: "firstName") | |
person.willChangeValue(forKey: "lastName") | |
person._firstName = "Mary" | |
person._lastName = "Taylor" | |
person.didChangeValue(forKey: "lastName") | |
person.didChangeValue(forKey: "firstName") | |
withExtendedLifetime(person) {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here's how I would do it. Basically it's more code and it sucks more, but that's how KVO is done unfortunately. It's a but like in the old Objective-C days when you had to write the stored properties yourself and did't have synthesised accessors. NEVER use the stored _properties from outside of the class, always use accessors or methods. The automatic "keyPathsForValuesAffecting" would trigger twice but sequentially if you don't use the new function that sets both at the same time, but that's ok and expected.