Let’s do something extremely simple:

We’ve got a view controller, it has a user, the user’s got a username, and we want to show that in our navbar title. With an @ in front, because that’s what all the cool kids do these days. Simple, right?

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = [NSString stringWithFormat:@"@%@", self.user.username];
}

Yep! That’s all it takes. As long as the username never changes.

Wait but what if it does

Well, if we’re the only one changing it, we could just re-set the title every time…

// A plausible thing what could happen
- (void)textFieldDidEndEditing:(UITextField *)textField {
    self.user.username = textField.text;
    self.title = [NSString stringWithFormat:@"@%@", self.user.username];
}

But that’s gross, as we’ve duplicated our formatting code. We could extract that into its own method:

- (void)updateTitle {
    self.title = [NSString stringWithFormat:@"@%@", self.user.username];
}

And then call that in viewDidLoad and textFieldDidEndEditing:. That’s a little better, but come on: why should textFieldDidEndEditing: know that the title depends on the username? That’s not its job. And what if we forget to call that the next time we change the username? Then we’re out of sync. Ugh.

This might not seem so bad, and it’s not… yet. But what if we wanted to change the username from a different view controller? Or as the result of a network response? This solution clearly isn’t going to scale. We’re going to need to try something else.

Let’s try KVO.

The Slow Descent into Madness

- (void)viewDidLoad {
    [super viewDidLoad];
    [self updateTitle];
    [self.user addObserver:self
                forKeyPath:@"username"
                   options:0
                   context:NULL];
}

Okay. We added ourselves as an observer to the user. But we didn’t specify a delegate, or a selector, or anything… so how are we going to know when it changes?

Some quick googling tells us that we have to override this method…

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context;

Well, that’s a little weird. It’s a method of the informal NSKeyValueObserving protocol that all NSObjects conform to. Okay. KVO is a pretty low-level feature; we’ll accept the weirdness.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    [self updateTitle];
}

Great! This works. And we can even make it a little nicer:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.user addObserver:self
                forKeyPath:@"username"
                   options:NSKeyValueObservingOptionInitial
                   context:NULL];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    self.title = [NSString stringWithFormat:@"@%@", self.user.username];
}

The NSKeyValueObservingOptionInitial flag makes it invoke the observation callback as soon as it’s added, so we can ditch the updateTitle method: there’s now only one place where we format the username.

That doesn’t look so bad! And some quick testing reveals that it approximates correctness pretty well. Let’s ship it!

Wait it’s full of bugs still

What if we’re observing multiple key paths of multiple objects? Well, then we need to be a little more careful in our implementation of the method.

We only want to update our title as a result of self.user.username changing. In this particular case, it doesn’t matter if we set self.title a little too often, but if we were performing side effects or something then it is important that we Do The Right Thing here:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    if (object == self.user && [keyPath isEqualToString:@"username"]) {
        self.title = [NSString stringWithFormat:@"@%@", self.user.username];
    } else {
        // ...?
    }
}

Ugh. String comparison, really? Yes. This is actually the way that you do this. Bear with me; it’ll get better. But first it has to get much, much worse.

There’s an annoying, subtle bug here. Do you see it? We never called super.

Should we? We know we should always call super if our superclass implements the same delegate methods… but this isn’t really a delegate method. It’s a special magic NSObject method. But UIViewController is an NSObject too, so maybe we should?

Apple’s Key-Value Observing Programming Guide advises us to “be sure to call the superclass' implementation if the superclass implements it.” But it doesn’t explain what “implements it” means. UIViewController instances certainly respond to the selector observeValueForKeyPath:ofObject:change:context:, but so do all NSObjects, so that’s not a useful data point. Does UIViewController use KVO?

We don’t know. And we can’t assume anything either way. Even if it doesn’t use it now, it could use it in the future, and we want our code to be future proof.

But we can’t just throw a super call up in there, because some of those messages are intended for us, and we don’t want to tell UIViewController about those. If we do, UIViewController will freak out, throw an NSInternalInconsistencyException, and our app will crash.

Careful Reader Chimes In

“Wait a second,” the careful reader says, “even if UIViewController ‘uses KVO,’ there’s no way that it’s observing self.user – UIKit doesn’t know about our model layer! We can just check if object == self.user and be done with it.”

Great point, careful reader. That is true, in this particular case, but we want to learn how to use KVO from any class – not just when we happen to be subclassing UIViewController. And I beg you consider the following scenario: what if someone subclasses us?

If someone comes along and makes an IHProfilePadViewController : IHProfileViewController, and they want to use KVO in the subclass, what happens? Are we suddenly going to see messages intended for our subclass? Is the subclass going to call super? It’s completely reasonable for IHProfilePadViewController to observe self.user, and we don’t want it screwing up our KVO and trying to handle our messages. We’re gonna need to figure out this inheritance thing eventually.

Let’s do it now.

Context-Free Coding

Remember that weird context parameter from the addObserver call? We passed in NULL, because when we googled around for KVO examples, everyone passed in NULL. Even Apple does it! And when we looked at the docs, we saw the following:

context

Arbitrary data that is passed to anObserver in observeValueForKeyPath:ofObject:change:context:.

Well, we didn’t really need to pass any data around, so we just sent NULL, felt a little weird that it’s a void * instead of an id, and went on with our day. What could go wrong?

Nothing. Until the day we start using KVO in different levels of our class hierarchy and everything breaks.

Because it turns out that the context argument is the only way to differentiate messages intended for your implementation from messages intended from your superclass’s implementation. Oh, you didn’t get that from the docs? Yeah. Me neither.

Here’s what we actually need to do:

// IHProfileViewController.m, in its entirety,
// because we've been doing stuff for a while
// and having lots of context is comforting.

#import "IHProfileViewController.h"

// The static keyword makes the variable file-scoped instead of global.
static const char kIHProfileViewControllerKVOContext;

@implementation IHProfileViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.user addObserver:self
                forKeyPath:@"username"
                   options:NSKeyValueObservingOptionInitial
                   context:&kIHProfileViewControllerKVOContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    if (context == &kIHProfileViewControllerKVOContext) {
        if (object == self.user && [keyPath isEqualToString:@"username"]) {
            self.title = [NSString stringWithFormat:@"@%@", self.user.username];
        } else {
            // assuming that we might use KVO elsewhere in this same file
        }
    } else {
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                              context:context];
    }
}

@end

Although that looks daunting, it’s mostly because it includes all the code we built up before. The only new thing in that code snippet is the handling of the context argument.

We created a variable, kIHProfileViewControllerKVOContext. I made it a char, but it could be an int or a void * or anything else – that doesn’t matter. We only care about its address.

Because, you see, its address is unique. And unless they get extremely lucky, no one is going to be able to guess that address – because nothing outside of this file even knows that that variable exists. Which means that if observeValueForKeyPath is called with that address as the context parameter, we can be very confident that it’s a message intended for us. And if we get a different context, we can be very confident that it’s intended for our superclass. (If it were intended for our subclass, we assume that they never would’ve supered it up to us in the first place.)

Cleaning Up

We’ve added this observer to the user. Odds are pretty good that the user object is going to stick around in memory longer than our view controller. And if that happens, the user will continue to try to notify us about changes to the username property, even after we’ve been deallocated.

If we’re lucky, we’ll see the following warning in the console:

Observation info was leaked, and may even become mistakenly attached to some other object.

If we’re unlucky, we’ll crash with an EXC_BAD_ACCESS the next time that user’s username changes and spend hours tearing our hair out trying to figure out why.

So we remember to clean things up in dealloc:

- (void)dealloc {
    [self.user removeObserver:self
                      keyPath:@"username"
                      context:&kIHProfileViewControllerKVOContext];
}

Okay! That’s not so bad. A single statement. We just need to remember to include it every single time we call addObserver. And if we rename the keypath, we have to be sure to rename it in two places.

Are you going to just keep adding code or what

No, we’re done! We have successfully set the title of our view controller to a nicely formatted username using KVO. To recap, we had to:

  • make a unique context variable for every class and use it in all add/removeObserver calls
  • check the context to decide if the message is intended for us or our superclass
  • check the object and keyPath to see what actual property is changing
  • remember to remove the observer once we’re done with it (probably in dealloc)

And that’s how to use KVO correctly.

I’m quitting iOS forever

So, still with me? Or are you unbelievably bored and deciding that there’s no way that you’re going to write this much boilerplate every single time you want to display one simple string in the navbar?

updateTitle is starting to look pretty good.

Which is conflicting! We want to love KVO: fully decoupled observation for practically any objects in your codebase? Sign me up! But the signup form is ten pages long and doesn’t even include the optional “please don’t crash my app” rider.

If only there were some way to leverage the power of KVO without having to endure Cocoa’s KVO API…

ReactiveCocoa to the Rescue

Wouldn’t it be cool if we could just say “Yo runtime, self.title should be self.user.username with an @ character in front, just do what I mean and handle all this other nonsense for me”?

Well, there’s a little project called ReactiveCocoa that lets us do exactly that.

- (void)viewDidLoad {
    [super viewDidLoad];
    RAC(self, title) = [RACObserve(self.user, username) map:^(NSString *username) {
        return [NSString stringWithFormat:@"@%@", username];
    }];
}

Alright, that’s a good start, but now we need to – oh. Wait. We don’t need to do anything else. That’s all we have to write. We don’t have to clean it up in dealloc – ReactiveCocoa automatically swizzled the dealloc method to take care of that for us. We don’t need to worry about contexts, or do any string comparison. It just… works.

Except it’s actually doing more, like static checking of the username key. If we typo that, instead of the code silently failing, we’ll get a compiler error. Plus we can use the ReactiveCocoa code in a category, unlike the vanilla KVO.

And you know what else? It’s clear. It’s declarative. It is, dare I say, beautiful.

Wait what that’s amazing tell me more

That’s about 5% of what ReactiveCocoa can do. Don’t even get me started. That was nothing.

There are enough concepts in those three short lines that it will take another blog post to do them justice. But I hope that I’ve piqued your curiosity enough that you’ll go investigate for yourself. How does that work? Why was it so easy? Why isn’t everyone using ReactiveCocoa all the time? How have I never heard of this before?

ReactiveCocoa is much more than a beautiful wrapper around KVO, but it’s a great gateway use case. Replace your brittle addObserver with a series of RACObserves and before you know it you’ll be composing signals like the best of them.