Introduction to Key-Value Observing in iOS

Today, we are going to talk about Key-Value Observing in iOS. In my last post, I introduced some Key-Value coding concepts. We learned about how to change an object’s properties and perform validation deep inside our data model so that we can lay a solid foundation for our application’s error handling. We also listed some disadvantages and potential problems so that we all know what to avoid when using this potent weapon in an iOS developer’s arsenal. So if you missed it, make sure you check it out afterwards.

What is Key-Value Observing?

Key-Value Observing is functionality that allows objects to get a notification whenever a specified property in another object gets modified. This means that every time that property is changed, another object, called an “observer” gets a notification (callback). All this is packed inside NSObject so, in general, it can be used for any Objective-C class.

Why would one choose to use Key-Value Observing?

Although delegation and NSNotifications are an excellent way to get notified that something important happened in another object, this might not always be the best solution. it might also be the case that, just like with Objective-C categories, your are not able (or willing) to modify an existing object in order to add a delegate or post an NSNotification. Key-Value Coding is also a more compact and easier way to get the job done in many cases. Let’s way you have a bunch of data objects and whenever, for some reason, one of those data objects changes, you have to synchronize the whole array with a server or local storage. You might think that, in the latter case you should be using CoreData – you can always get notified in case of changes like that. This is true, but consider this – CoreData will only notify you that an object has changed, and it will trigger for all changes in the object. If you wanted to synchronize only if certain properties changed, you would have to make some extra effort. Returning to the example above, you are not left with many options. It seams like an overkill (and it doesn’t feel right) to have a delegate of a data class. Also, who would be the delegate? What if several objects want to be the delegate? NSNotifications fix the second problem, but it is still not a very good fix. Now, with Key-Value Observing, whoever is interested in update notifications (and has a reference to the data object) can add itself as an observer and specify which properties it is interested in. It’s that simple!

Which objects support Key-Value Observing?

Almost any NSObject subclass. In fact, It is built into NSObject itself. Every time you create a property using @property, that property is eligible for key value observing. Additionally, all NSManagedObjects DO support it.

How does one register as an observer?

Just add the following code:

[object addObserver:self // self will receive the callback
forKeyPath:propertyName // Changes in the property with name forKeyPath trigger callbacks
options:NSKeyValueObservingOptionNew
context:nil];

Similarly, stop receiving notifications by calling:

[object removeObserver:self forKeyPath:propertyName];

Where does one receive callbacks from an observer?

You need to implement the following method:

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

In it, you have access to the property name that has been changed via the “keyPath” parameter and to a reference to the object that has been changed. In this method’s body, you can insert code that handles the change appropriately.

That’s all you need to know to start observing!

Let me know in the comment section how do YOU use Key-Value Observers and what problems you were able to overcome with it.

Thanks for reading.

Using Key Value Coding validation

Key value coding in a powerful tool in Objective-C’s Cocoa framework that allows you to change an NSObject subclasses’s data without using properties and setters. It uses a mechanism, similar to the one used for adding data to an NSMutableDictionary. For instance, if I wanted to change a property named “brand” in a “Car” class, using properties, I would write something like:

car.brand = @"Honda";

Using Key Value Coding, or KVC,  I can achieve the same thing typing:

[car setValue:@"Honda" forKey:@"brand"];

At first glance, the latter might seam like it doesn’t make much sense. First of all, it introduces some hardcoded strings, which is never a good thing. Not only that, hardcoding a property’s name can be very dangerous if we later decide to change that property’s name, or remove it altogether. If this happens, the application will crash with an exception, saying the Car in not key value compliant for key “brand”.

Another disadvantage of using KVC is that it might seam like it decreases dependency in the code because you would only need to use NSObject (KVC is built into NSObject). In the example above, we don’t need to expose the Car class, we are only setting a property in an object of a class, we don’t need to know about. However, the fact that the header file for Car is not imported, doesn’t mean that there is no dependency. We still have to be sure that the object is an instance of the Car class, or at the very least, make sure it has a property named “brand”.

Despite this, Key value coding can be a powerful weapon if used appropriately.  It adds a new level of abstraction when dealing with different types of data classes. Normally, it would be hard to generalize some code to work for a wide range of data classes. Let’s say you want to be able to dynamically map values to a data model by using plists. It would be inconvenient to put method names in it (it’s possible, but it doesn’t seam right – to me anyway). Using KVC, you will be able to put property names in that plist in order to do the mapping. And since you made the effort to implement reading those plists, you will most likely want to be able to use not only one type of data class, but a wide range of them. And since KVC is built into NSObject, you will have no problem doing so – all you need to know is the name of the property you need to change.

Another major advantage in using KVC is the ability to integrate validation and error handling right into your data model. The problem with error handing is that it’s too boring and tedious and programmers tend to avoid it like the plague until the last stage of development. This means that often it is not considered during the design phase and often it becomes too hard to implement properly. The harsh reality is that software has to be designed with error handing in mind. You have to carefully think about, how errors are generated and passed along from the business logic to the UI. This is not as easy as it might seam sometimes. Key value coding validation allows you to integrate error checking right into your data model using its validate methods. The idea behind validation methods is pretty straight-forward. For every property, a method called validate<PropertyName>:forKey:error: contains code that checks if the new value supplied is valid for that property. It returns YES or NO depending on whether or not the value is acceptable and if not, an error can be returned in the last parameter.  Additionally, the value, provided to the method via the first parameter can be modified during the function’s executing. The idea is that, if validation fails, a different value might be supplied that will not fail the checks but is semantically identical to the one provided. For example – let’s say a numeric field is being validated, but the user has specified a string (@”15 km”) instead of a number. The validation method realizes that it received an object of the wrong type, but instead of returning NO and passing an NSError object, it first tries to convert the string to a number, just in case it contains same valid information. In this case, it would find the number 15 in the string and decide to use that. It might even realize that the user has specified kilometers and it needs a distance in meters and multiply 15 by 1000 to get 15000 as a final result. This can, no doubt, be useful in many applications. Unfortunately, Key value coding validation will not automatically invoke the validate method every time a property setter is called. You have to explicitly validate the value before calling the setter.

NSError* validationError = nil;

[accident validateValue:&valueToValidate forKey:propertyKey error:&validationError];

if (validationError == nil)

{

[accident setValue:valueToValidate forKey:propertyKey];

}

The down side of using validation methods is that you have to write one for every property of every data classes you wish to use. That can be extremely tedious. So tedious that I wrote a bash script that generates them for me. It recognizes common field types such as NSString*, NSDate and NSNumber and creates a template suitable for that type. For all other data types, it just creates the method definition returning YES by default. In order to use it, just supply your .h file (containing the @property definitions) and the script will dump all validate method implementations in the console. If you are interested, here’s the code:

#!/bin/bash

function capitalizeFirstLetter

{

word=$1

word=”$(tr ‘[:lower:]’ ‘[:upper:]’ <<< ${word:0:1})${word:1}”

}

fileName=$1

allProperties=$(grep @property $fileName)

grep @property $fileName |  while read -r property

do

# handle string properties

isString=$(echo $property | grep NSString)

propertyName=$(echo $property | rev | cut -d’ ‘ -f 1 | rev | cut -d’;’ -f 1)

propertyName=”$(tr ‘[:lower:]’ ‘[:upper:]’ <<< ${propertyName:0:1})${propertyName:1}”

if [[ ! -z $isString ]]

then

echo -e “- (BOOL)validate$propertyName:(NSString**)value\n error:(NSError**)validationError\n{\n\tif ((*value).length == 0)\n\t{\n\t\tNSError* error = [NSError errorWithCode:1\n\t\t domain:(NSString*)??? \n\t\tdescription:NSLocalizedString(@\”???\”, @\”\”)];\n\t\t*validationError = error;\n\t\treturn NO;\n\t}\n\treturn YES;\n}”

fi

# handle date properties

isDate=$(echo $property | grep NSDate)

if [[ ! -z $isDate ]]

then

echo -e “- (BOOL)validate$propertyName:(NSDate**)date\n error:(NSError**)validationError\n{\n\tif (*date == nil)\n\t{\n\t\tNSError* noDateError = [NSError errorWithCode:1\n\t\t domain:(NSString*)??? \n\t\tdescription:NSLocalizedString(@\”???\”, @\”\”)];\n\t\t\t*validationError = noDateError;\n\t\t\treturn NO;\n\t\t}\n\t\telse if( [*date compare:[NSDate date]] == NSOrderedDescending )\n\t\t{\n\t\t\tNSError *futureDateError = [NSError errorWithCode:1\n\t\t domain:(NSString*)??? \n\t\tdescription:NSLocalizedString(@\”???\”, @\”\”)];\n\t\t\t*validationError = futureDateError;\n\t\t\treturn NO;\n\t\t}\n\t\treturn YES;\n}\n”

fi

isArray=$(echo $property | grep “Array”)

if [[ ! -z $isArray ]]

then

echo -e “- (BOOL)validate$propertyName:(NSArray**)array\n\t\terror:(NSError**)validationError\n{\n\tif ((*array).count == 0)\n\t{\n\t\t// Must have at least one object\n\t\tNSError* noDateError = [NSError errorWithCode:1\n\t\t domain:(NSString*)??? \n\t\tdescription:NSLocalizedString(@\”???\”, @\”\”)];\n\t\t*validationError = noDateError;\n\t\treturn NO;\n\t}\n\treturn YES;\n}\n”

fi

if [[ -z $isString && -z $isArray && -z $isDate ]]

then

echo -e “- (BOOL)validate$propertyName:(NSObject**)value\n\t\terror:(NSError**)validationError\n{\n\t// TODO: implement validation for $propertyName \n\treturn YES;\n}\n”

fi

echo -e ‘\n’

done

It’s not going to win any prizes, but it works for me.

The good news is that validation methods are the only ones that you have to explicitly define. Setters and getters are handled by the methods generated by @property and @synthesize.

I guess that’s it. That’s all you need to do to use Key value coding validation. I hope you found this tutorial useful and think about times when you could have used key value observing for your applications. In my next post, I’ll be covering Key-Value Observing – another great, more advanced feature of the Cocoa framework. Thanks for reading.