The state of KVO in Swift

While everyone agrees that the API for Key-Value Observing in Objective-C is terrible, it is also an essential part of a Cocoa developer’s toolbelt, especially when considering that many system provided frameworks rely on it to expose parts of their functionality.

Swift doesn’t allow us to observe properties of another object and respond to changes to that property like KVO does. It does however allow us to hook into the Cocoa implementation of KVO by virtue of Swift’s interoperability with Objective-C. In this article I illustrate how to use KVO from within Swift classes, and I offer an alternative that is more native to the language and takes advantage of Swift’s strengths.

Let’s start by defining the conditions that must be met in order to use KVO from a Swift class:

  1. Observer must inherit from NSObject.
  2. Observed object must either be defined in Objective-C or it must be defined in Swift and inherit from NSObject.
  3. If the observed object is defined in Swift. The dynamic modifier must be added to any property you want to observe.

The first two conditions are fairly obvious, given that NSObject implements the methods defined by the NSKeyValueObserving protocol. The third condition might require some additional explanation.

Dynamic dispatch

The reason that Swift requires a dynamic modifier to be added to any observable property is that even though your object must inherit from NSObject, this does not guarantee that Swift will use dynamic dispatch, which is what KVO relies on to perform its magic. This is because the Swift compiler may attempt to optimize your code by bypassing the Objective-C runtime. The dynamic modifier is just a way to opt out of this behavior.

Improved syntax

Now that we understand the requirements for implementing KVO in Swift, let’s see if we can do something to improve the syntax. After all, we wouldn’t want our new and shiny Swift code to be contaminated by this ugly API.

A typical implementation of KVO in Swift would look something like this:

https://gist.github.com/njdehoog/d0cc028b48d0a43d173c

That works fine, but if we were to observe more than one property or more than one object, this code would soon become bloated and unpleasant to read. It would be much nicer if we could abstract the observing mechanism into a separate class, so that we could reduce the amount of boilerplate code we have to reproduce:

https://gist.github.com/njdehoog/7cbc7300dba4cbd490e9

This approach lets us set up Key-Value Observing by simply initializing an instance of KeyValueObserver with a trailing closure, which is invoked when changes occur.

Notice that because our KeyValueObserver object handles the KVO registration for us, the actual observer no longer needs to inherit from NSObject.

It is important to note that there are a few caveats to this approach. If you are referencing self from within the closure, make sure to include a capture list to prevent a strong reference cycle. In addition you need to make sure to release the instance of KeyValueObserver before the observed object is deallocated.

Native Swift approach

While being able to use KVO from Swift can come in handy, what we really want moving forward is a more native Swift approach. In Swift we can’t manipulate the runtime like we can in Objective-C, but we did get a new language feature called Generics. We can take advantage of Generics to provide functionality that is similar to KVO.

Let’s first define what we want our code to look like and then dive in to how we make that happen.

https://gist.github.com/njdehoog/b881e127b17f4d4c03fe

The first thing to notice here is that we will need to wrap the property we want to observe in a struct called Observable. This will allow us to act on any changes that are made to the value of that property.

The syntax for subscribing to changes is rather simple. We can just call subscribe on the property we want to observe, and pass a closure for handling the changes.

Let’s examine how this works under the hood.

https://gist.github.com/njdehoog/3d0a0d9c83ad32e8fdb8

If you haven’t worked with Generics before, this code might look rather convoluted, but once we break it down, there isn’t that much to it.

The main player in this setup is a struct called Observable. This struct wraps our property’s value and exposes methods for subscribing and unsubscribing to changes. In the struct declaration an unknown type of T is defined. This is what allows our struct to work with any type, while retaining the type safety that we want.

The Subscription class represents, as the name suggests, a subscription to a particular instance of Observable, and the Subscriptions struct manages these subscriptions. The reason that Subscription is implemented as a class rather than a struct is that this allows us to use the identity equality operator (===) to find a particular subscription in our array of subscriptions.

Notice that the Subscriptions struct implements the SequenceType protocol. This allows us to iterate over the subscriptions with a for loop.

Last but not least, the ValueChangeEvent, represents a value change event, and contains the old value and the new value for the observed property.

Hopefully this overview can provide a head start for those of you who are used to working with KVO in Objective-C and are struggling to adjust to the new world of Swift like I am. If you are interested in using the native approach as described above, please check out the Observable-Swift project on Github, which I used as inspiration for this article.

Find more of my writing at nielsify.com

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store