Key-Value Coding Is Important.

Key-Value Coding and Key-Value Observing are really powerful concepts found throughout Apple’s frameworks. They allow you to write code more dynamically and even observe and act accordingly when properties in your classes change. This can be really useful, because you can do anything when a property changes, including but not limited to check if its value is valid, send a message to someone when something changes to a certain value and so on. The options are unlimited.

Key-Value Coding (KVC) and Key-Value Observing (KVO) are key when writing Mac OS X applications. Cocoa Bindings allow you to wrap your variables with Cocoa objects and this is done via KVC and KVO (note that iOS does not support Cocoa Bindings – instead, it uses the classic target-action concepts you already know). Cocoa Bindings are really powerful and very easy to work with.

KVC and KVO are found throughout many Apple frameworks for both iOS and Mac OS X. I will show you some examples of frameworks that make use of KVC and KVO in both iOS and Mac OS X so you can see how important they really are.

For some reason, KVC and KVO are tough topics to understand for many people, regardless of how simple they really are. In this tutorial, I will teach you KVC and KVO, and I will leave Cocoa Bindings as a little extra for those interested – it is not obligatory reading. If you’re interested in writing iOS apps and just iOS apps then Cocoa Bindings are irrelevant to you. But if you plan on writing more code for Apple and move on to the Mac, you may benefit a bit from that section.

Finally, before moving on, this post is for both Objective-C newcomers and people who have been writing code for way longer. No matter your experience, there is a slight chance you will get something out of this small tutorial.

‘Aight, let’s get started!

Key Value Coding: What Is It And Why Is It Needed?

KVC is a form of coding that allows you to access an object’s properties indirectly, using strings to access them instead of the property’s accessors or instead of accessing the variables directly.

To enable such mechanism, your classes must comply to the NSKeyValueCoding informal protocol. Luckily for you, NSObject complies with it and it also has default implementations of its methods, so most of the time you won’t need to modify anything in order to use KVC.

We are going to write KVC code shortly.

Why Would Anyone Want To Do That? How Can This Be Applied?

This is a very important question. What’s the point in using KVC to access properties when you can just use the accessors to do it?

Firstly, KVC allows you to do KVO: That is, if you are observing a variable, you will only be notified of its changes if and only if the changes take place using KVC. If you modify a variable directly using its setter you are not going to be notified of its changes. Does this sound abstract? Don’t worry if you don’t understand this paragraph just yet. You are going to understand everything a lot more once we get to the KVO section.

Secondly, like I said at the beginning of this tutorial, many of Apple’s frameworks use it for both iOS and Mac OS X. Core Data, for example, is one of the most powerful frameworks of Apple’s and it can use KVC, not to mention it is available in both Mac OS X and iOS. If you are using the NSManagedObject class instead of your own subclass of it, you “must” use KVC to access your Core Data fields with the object.

NSManagedObject *character; //Lets assume the core data object has been fetched.

//Giving the object some values to save later.
[character setValue:@"Sakura Kinomoto" forKey:@"characterName"];
[character setValue:38 forKey:@"ownedCards"];

//Fetching those values using KVC.

NSString *sakura = [character valueForKey:@"characterName"];
NSInteger numberOfClowCards = [character valueForKey:@"ownedCards"];

KVC can be avoided when using Core Data if you subclass NSManagedObject. Regardless, it’s important to know what’s happening under the hood.

So enough yaddas. Let’s write some KVC code. Go to Xcode and create a new Command-Line Tool project (please note I hate giving explicit instructions such us “Launch Xcode, navigate to file, a new window will show up, go to X section, and select Command-Line tool” because for all my tutorials I assume you are familiar with your tools and you should know how to use them without instructions). I named my project “CardCaptorChars”.

A couple of things need to be changed before we move on with the example. First, rename main.cpp to main.m. Then delete all the code in this file and just write the basic skeleton of the program. Don’t forget to import the Foundation framework:

#import <Foundation/Foundation.h>

int main()
{

}

Afterwards, create an Objective-C class with a couple of properties and methods (feel free to do it in the same file. The point of this tutorial is not to show how can files be separated and such). Here’s mine:

@interface Character : NSObject
{
	NString *characterName;
	NSInteger ownedClowCards;
}
@property (nonatomic, copy) NSString *characterName;
@property NSInteger ownedClowCards;
-(void)hasLostClowCard;
-(void)hasGainedClowCard;
@end

If you’re following with my example, here’s the implementation:

@implementation Character
@synthesize characterName;
@synthesize ownedClowCards;

-(void)hasLostClowCard
{

}

-(void)hasGainedClowCard
{

}

@end

The methods are going to be filled in the KVO section.

Now we are going to create some objects and set and get the variables using KVC. So lets implement main():

int main()
{
	Character *sakura;
	Character *shaoran;

	//Create and give the properties some values with KVC...
	sakura = [[Character alloc] init];
	[sakura setValue:@"Sakura Kinomoto" forKey:@"characterName"];
	[sakura setValue:[NSNumber numberWithInt:20] forKey:@"ownedClowCards"];

	shaoran = [[Character alloc] init];
	[shaoran setValue:@"Li Shaoran" forKey:@"characterName"];
	[shaoran setValue:[NSNumber numberWithInt:21] forKey:@"ownedClowCards"];

	//Done! Now we are going to fetch the values using KVC.

	NSString *mainCharacter = [sakura valueForKey:@"characterName"];
	NSNumber *mainCharCards = [sakura valueForKey:@"ownedClowCards"];

	NSString *rival = [shaoran valueForKey:@"characterName"];
	NSNumber *rivalCards = [shaoran valueForKey:@"ownedClowCards"];

	NSLog(@"%@ has %d Clow Cards", mainCharacter, [mainCharCards intValue]);
	NSLog(@"%@ has %d Clow Cards", rival, [rivalCards intValue]);
}

And that’s it! Setting and getting variables with KVC is really, really straightforward. It is a bit more of work but it is needed in some cases.

Key Value Observing

The main reason you would want to work with KVC is to observe object properties when they change. KVO simply allows you do to that. Any object can observe any variable in any “key path”.

But First, a word on Key Paths.

What’s a key path? The name “Key Path” sounds intimidating. Actually, I got scared when I first saw that term. A Key Path is simple a “full route” to a property. “self.name” is the full key path to self’s name property. If you have a person that has a Clock that has a Time with a given Hour, its key path would be “clock.time.hour”.

Sakura has 20 Clow Cards and Shaoran has 21. Wouldn’t it be interesting to know when they lose or earn a new Clow card? At least our program could tell us when such a thing happens.

Implementing KVO is as easy as adding an observer for the desired key path and implementing a fixed method that gets called whenever the observer notices changes in its observed variable. To register an object as an observer you use NSKeyCodingProtocol’s addObserver:forKeyPath:options:context: method. The observeValueForKeyPath:ofObject:change:context: method is called whenever a change takes place.

So first, you need to add this method to your class:

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

}

This is the essential method that gets called when the value changes. You can also get notified when the value at the key path is about to change and you can get notified after the change takes place, using the methods willChangeValueForKey and didChangeValueForKey respectively. We are not going to implement these methods but their use is really straightforward.

Once you do, we are going to register Sakura to observe her own changes. You could naturally ask Shaoran to be notified when Sakura’s Clow card counter changes and vice-versa. Add this after declaring the characters.

[sakura addObserver:sakura forKeyPath:@"ownedClowCards" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

I want you to pay special attention to the options. NSKeyValueObservingOptionNew and NSKeyValueObservingOptionOld. With these two options we are specifying the the change NSDictionary should contain both the new value and the old value. In our case we need to know both to know if the Clow card count has increased or decreased.

A little note. At the end of your program make sure you remove Sakura as her own observer. Otherwise the console prints a nasty message.

[sakura removeObserver:sakura forKeyPath:@"ownedClowCards"];

We are going to implement our two empty methods now:

-(void)hasLostClowCard
{
	NSLog(@"%@ has lost a card! Cards now: %ld", characterName, ownedClowCards);
}

-(void)hasGainedClowCard
{
	NSLog(@"%@ has earned a card! Cards now: %ld", characterName, ownedClowCards);
}

Very simple, but lets keep it like that for the sake of demonstration.

Now we are going to implement observeValueForKeyPath:ofObject:change:context: in such a way that hasLostClowCard gets called when the character has less cards than before and hasGainedClowCard when the character has more. Like I told you before, the options define what will be passed in the change dictionary, so make sure you use the Apple documentation when you need it.

The implementation simply looks like this:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	if([keyPath isEqualToString:@"ownedClowCards"])
	{
		NSInteger oldC = [change objectForKey:NSKeyValueChangeOldKey];
		NSInteger newC = [change objectForKey:NSKeyValueChangeNewKey];
		if(oldC < newC)
		{
			[self hasGainedClowCard];
		}else
		{
			[self hasLostClowCard];
		}
	}
}

And that’s all. It’s pretty straightforward: First we make sure that the key we are observing is the one being changed. If it is, We save the old and new values in variables, and we compared them to see what message should be sent.

This is really, really powerful. Having control even on the change of your properties is a really powerful feature and I’m sure you will find great cases to use in your own projects.

As usual, you can find the source code of the above project on my Pastebin here. And of course, feel free to ask questions shall you have them.

A Little Extra: Cocoa Bindings in OS X.

If you are just writing iOS apps, the tutorial ended with section 2. If you are a Mac Developer too (or want to be), then you should read this section to understand how Cocoa Bindings work with KVC and KVO and how powerful they can be.

Cocoa Bindings is about binding a variable to a GUI object, so when one changes, the other changes as well and are therefore “kept in sync”. Using Cocoa Bindings is a way of creating target-action for whenever you do something with your GUI objects.

To show you how Cocoa Bindings work, we are going to create a Mac app with a slider and an NSLog() that will let us know whenever the slider has changed and the value it changed to. So create a new Mac Cocoa Application with Xcode and drag a slider to it. The slider should have a range of 0 to 100. You should have something like what I have below:

Now on your app delegate’s header add a new property called sliderValue.

@property NSInteger sliderValue;

And don’t forget to synthesise it.

(Not showing full code here, because again, I expect you know your way around this stuff for now. But as usual, you will find a link to download the example project at the end of this tutorial.

Now open the Bindings Inspector. The bindings inspector is the icon that looks weird, and is the second to last icon. Picture below:

Expand the Value drop down. Check the “Bind to” checkbox and the select “App Delegate” in the drop down menu next to it. Now finally, write “self.sliderValue” in Model Key Path.

We just bonded sliderValue with our slider so they will be kept in sync whenever one changes. We still won’t receive notifications when there are changes, so register the App Delegate to observe sliderValue as well.

[self addObserver:self forKeyPath:@"self.sliderValue" options:NSKeyValueObservingOptionNew context:nil];

And implement observeValueForKeyPath:ofObject:change:context:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	NSInteger newVal = (NSInteger)[change objectForKey:NSKeyValueChangeNewKey];
	NSLog(@"changed %ld", newVal);
}

Now you just bonded both items together without having to do much effort! There’s many other things you can do with Cocoa Bindings – You could, for example, keep the slider and a label in sync at the same time. Everything you would need to do is to create the label and observe its title property.

And that’s it! I hope this tutorials helps you understand some nifty concepts better. KVC and KVO are important concepts that everyone should know.

CHANGELOG:


September 13, 2012 at 8:00 PM [America/La_Paz]: First version of this tutorial.

Positive SSL