Last active
July 26, 2016 22:43
-
-
Save daehn/4842a9c9233f7e7ade37 to your computer and use it in GitHub Desktop.
Helper class to provide a typed, closure based KVO interface in Swift
This file contains 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 | |
struct Change<T>: CustomDebugStringConvertible { | |
let oldValue, newValue: T? | |
var debugDescription: String { | |
let prettyString: T? -> String = { return $0 != nil ? "\($0!)" : ".None" } | |
return "ChangeType:\n\tOld value: \(prettyString(oldValue))\n\tNew value: \(prettyString(newValue))" | |
} | |
} | |
final class Observer<T>: NSObject { | |
private let context = UnsafeMutablePointer<Void>() | |
typealias ChangeBlock = Change<T> -> Void | |
weak var object: AnyObject? | |
var keyPath: String | |
private var changeBlock: ChangeBlock | |
private var tornDown = false | |
init(object: NSObject, keyPath: String, changeType: T.Type, onChange: ChangeBlock) { | |
self.object = object | |
self.keyPath = keyPath | |
changeBlock = onChange | |
super.init() | |
object.addObserver(self, forKeyPath: keyPath, options:[.New, .Old], context: context) | |
} | |
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { | |
guard keyPath == self.keyPath && context == self.context else { return } | |
changeBlock(Change<T>(oldValue: change?["old"] as? T, newValue: change?["new"] as? T)) | |
} | |
private func tearDown() { | |
object?.removeObserver(self, forKeyPath: keyPath, context: context) | |
tornDown = true | |
} | |
deinit { | |
assert(tornDown, "Deinit called wihtout calling tearDown first") | |
} | |
} | |
// Usage: | |
class Person: NSObject { | |
dynamic var name: String? | |
} | |
let max = Person() | |
let observer = Observer(object: max, keyPath: "name", changeType: String.self) { change in | |
print(change) | |
} | |
max.name = "Max" | |
max.name = "Bruno" | |
max.name = "Pierre" | |
observer.tearDown() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment