KVO in swift

key-value observing (KVO), and it effectively lets you say, “please tell me when the property X of object Y gets changed by anyone at any time.”

We’re going to use KVO to watch the estimatedProgress property, we add ourselves as an observer of the property on the web view by adding this to viewDidLoad():

webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)

The addObserver() method takes four parameters: who the observer is (we’re the observer, so we use self), what property we want to observe (we want the estimatedProgress property of WKWebView), which value we want (we want the value that was just set, so we want the new one), and a context value.

forKeyPath and context bear a little more explanation. forKeyPath isn’t named forProperty because it’s not just about entering a property name. You can actually specify a path: one property inside another, inside another, and so on. More advanced key paths can even add functionality, such as averaging all elements in an array! Swift has a special keyword, #keyPath, which works like the #selector keyword you saw previously: it allows the compiler to check that your code is correct – that the WKWebView class actually has an estimatedProgress property.

context is easier: if you provide a unique value, that same context value gets sent back to you when you get your notification that the value has changed. This allows you to check the context to make sure it was your observer that was called. There are some corner cases where specifying (and checking) a context is required to avoid bugs, but you won’t reach them during any of this series.

Warning: in more complex applications, all calls to addObserver() should be matched with a call to removeObserver() when you’re finished observing – for example, when you’re done with the view controller.

Once you have registered as an observer using KVO, you must implement a method called observeValue(). This tells you when an observed value has changed, so add this method now:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "estimatedProgress" {
        progressView.progress = Float(webView.estimatedProgress)
    }
}

As you can see it’s telling us which key path was changed, and it also sends us back the context we registered earlier so you can check whether this callback is for you or not.

In this project, all we care about is whether the keyPath parameter is set to estimatedProgress – that is, if the estimatedProgress value of the web view has changed. And if it has, we set the progress property of our progress view to the new estimatedProgress value.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s