Write yourself KVO in Swift
KVO is a major part of Objective-C dynamic nature. Having virtually every property or ivar observable by its key path opens doors to simple bindings or advanced technologies like ReactiveCocoa.
But what about Swift? It’s static nature shuts the possibility to observe properties of any class not willing to do so (by means of willSet
and didSet
). Not nice.
If we are going to observe something static, we need at least two things: a list of names of observable fields and a way to get their values by that name. Reminds of anything? Of course, that’s what Dictionary
does.
Let’s define a goal. Assume we have a Swift dictionary of Any
, and we want to add some observer block bound to particular key to be called when the value of that key gets changed. As a first step we will ignore the complexity of key-path navigation and start with a single key observation.
What do we need? First, a class to wrap our Dictionary
and a list of observers:
class ObservableDictionary {
typealias Observer = Any -> ()
private var dict = [String: Any]()
private var observers = [String: [Observer]]()
}
Then a method for adding observer:
extension ObservableDictionary {
func addObserver(key: String, observer: Observer) {
var list = observers[key] ?? []
list.append(observer)
observers[key] = list
}
}
And finally a setter that will trigger all observers registered for given key:
extension ObservableDictionary {
subscript(key: String) -> Any {
get {
return dict[key]
}
set(value) {
dict[key] = value
if let list = observers[key] {
map(list) { $0(value) }
}
}
}
}
And that’s it! Let’s test it:
let dict = ObservableDictionary()
dict.addObserver("name") {
newValue in
println("New name: \(newValue))")
newValue
}
dict["age"] = 30
dict["name"] = "Someone"
Simple, huh? Yet the big part is yet to be done - making this stuff work with real key paths. That will be a subject for my next post.
You can download Swift playground for this post