I have created an observer with .Old | .New
options. In the handler method I try to fetch before after values, but compiler complains: 'NSString' is not convertible to 'NSDictionaryIndex: NSObject, AnyObject
override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [NSObject : AnyObject]!, context: UnsafeMutablePointer<Void>) {
let approvedOld = change[NSKeyValueChangeOldKey] as Bool
let approvedNew = change[NSKeyValueChangeNewKey] as Bool
iOS 11 and Swift 4 brings significant changes to KVO.
@objcMembers
annotation in order to enable the KVO or KVO fails silently.dynamic
. Here is newer implementation,
@objcMembers
class Approval: NSObject {
dynamic var approved: Bool = false
let ApprovalObservingContext = UnsafeMutableRawPointer(bitPattern: 1)
override init() {
super.init()
addObserver(self,
forKeyPath: #keyPath(approved),
options: [.new, .old],
context: ApprovalObservingContext)
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
guard let observingContext = context,
observingContext == ApprovalObservingContext else {
super.observeValue(forKeyPath: keyPath,
of: object,
change: change,
context: context)
return
}
guard let change = change else {
return
}
if let oldValue = change[.oldKey] {
print("Old value \(oldValue)")
}
if let newValue = change[.newKey] {
print("New value \(newValue)")
}
}
deinit {
removeObserver(self, forKeyPath: #keyPath(approved))
}
}
There is also new bock based api for KVO, which works like this,
@objcMembers
class Approval: NSObject {
dynamic var approved: Bool = false
var approvalObserver: NSKeyValueObservation!
override init() {
super.init()
approvalObserver = observe(\.approved, options: [.new, .old]) { _, change in
if let newValue = change.newValue {
print("New value is \(newValue)")
}
if let oldValue = change.oldValue {
print("Old value is \(oldValue)")
}
}
}
}
Block based api look super good and easy to use. Also, KeyValueObservation is invalidated when deinited, so there is no hard requirement for removing observer.
With Swift 2.0, here is a complete implementation for a class that uses KVO,
class Approval: NSObject {
dynamic var approved: Bool = false
let ApprovalObservingContext = UnsafeMutablePointer<Int>(bitPattern: 1)
override init() {
super.init()
addObserver(self, forKeyPath: "approved", options: [.Old, .New], context: ApprovalObservingContext)
}
override func observeValueForKeyPath(keyPath: String?,
ofObject object: AnyObject?,
change: [String : AnyObject]?,
context: UnsafeMutablePointer<Void>) {
if let theChange = change as? [String: Bool] {
if let approvedOld = theChange[NSKeyValueChangeOldKey] {
print("Old value \(approvedOld)")
}
if let approvedNew = theChange[NSKeyValueChangeNewKey]{
print("New value \(approvedNew)")
}
return
}
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
deinit {
removeObserver(self, forKeyPath: "approved")
}
}
let a = Approval()
a.approved = true