How to use Key-Value Observing in Objective-C

An oft forgotten feature of Cocoa and Cocoa Touch development is Key-Value Observing, or KVO. It’s an ingenious system that lets us notify one class if something changes in another class. Or more specifically, if an object’s property changes in one instance, another object can react to it.

This sounds way more complicated than it really is. Think of all those times when you write a protocol, or create an NSNotification to tell another class to do something. Both those approaches are valid, and sometimes necessary, but there is a simpler alternative that I often forget when I’m hacking away.

Here’s how it works, shown with a simple example.

Imagine that Tabbed Application template in Xcode. You have two view controllers, and imagine there’s a text field in the first view controller. Someone enters text, and you’d like the same text to appear in the second view controller.

First View Controller

The ingredients we need in the first view controller are:

  • a reference to the second view controller
  • hence we need to import its header file
  • a UITextField property
  • an NSString property to hold our “message” that we want the second view controller to react to

We’ll also conform to the text field protocol so we can dismiss the keyboard appropriately:

#import "FirstViewController.h"
#import "SecondViewController.h"

@interface FirstViewController () 
@property (strong, nonatomic) IBOutlet UITextField *textField;
@property (nonatomic, strong) NSString *theMessage;

Registering an Observer

The first view controller needs to register an observer for the second view controller, and tell it which property (or key-value pair) should be observed. It’s a message that’s sent under the hood if our property changes.

In viewDidLoad, we’ll grab a reference to the second view controller and do just that:

- (void)viewDidLoad {
    [super viewDidLoad];
    // grab a reference to the second view controller
    SecondViewController *second = self.tabBarController.viewControllers.lastObject;
    [self addObserver:second forKeyPath:@"theMessage" options:NSKeyValueObservingOptionNew context:nil];

How you get the reference to your other object is dependent on your own app’s design of course. In the case of Apple’s Tabbed Application template, we can reach up to the navigation controller and grab the last object (i.e. the second view controller).

Notice the options parameter in this method: we can either pass the new (updated) value of our property (NSKeyValueObservingOptionNew), or we can see what the value was before it was changed (NSKeyValueObservingOptionOld). There are times where we may need that, but not today.

The keyPath parameter is equivalent to the property in our first view controller.

All that remains to be done here is to read out the text field and change our property with its contents. We’re implementing the UITextFieldDelegate protocol, so as soon as the return key is pressed on the keyboard, we can react and read the value:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    // read out the text field
    self.theMessage = textField.text;
    // and dismiss the keyboard
    [textField resignFirstResponder];
    return YES;

Second View Controller

The implementation for the second view controller’s reaction is even easier: all we have to do is implement a single method, which will be called when the first view controller’s property changes:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    // retrieve the first view controller's message
    NSString *message = [(NSString *)object valueForKey:@"theMessage"];
    NSLog(@"The message was %@", message);

Yes, seriously. That is it. Second (seemingly unconnected) view controller is notified and this method is called. It’s like magic!

Just to let you know: some parent classes may implement code in this method (NSObject does not, but other classes might). If that’s the case, call the parent’s super method like this:

// if implemented in the parent class, call this:
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];

Note that your app will crash if the parent class does not implement this method though. Thanks to magnett on GitHub for bringing this to my attention 😉

Try out KVO next time you need to send some data from one object to another. We’ve barely scratched the surface of what you can do with this feature though. Check out Apple’s documentation on how to implement it in your own classes.

Does this work for all classes?

If the class is a child of NSObject, then yes – KVC and KVO are implemented and ready for use.

Does this work in Swift?

Apparently so, but I haven’t tried it myself. Check out this thread for more information.

Demo Project

Check out a fully working demo project on GitHub:

Further Reading

2 thoughts on “How to use Key-Value Observing in Objective-C

  1. Small correction,
    Dot notation also works, this should work too: self.theMessage = textField.text. Dot notation is just syntactic sugar over [self setTheMessage:…], if it is not working look for error elswhere

    1. Well spank my tush and call me Charlie: today dot notation works just fine! I tried it for a whole day when I wrote this and for some reason it never triggered the observer. Thanks for letting me know Mindaugas!

Leave a Reply