Saturday, August 27, 2011

An Introduction to iOS Categories

Have you ever wished that you could add one, or a couple, additional functions to an Objective-C core class?

Well Apple has thought of this, and they provide a way without extended the class! The way to do this is called a category. A category is a way to enhance an existing class, by adding additional functions to it. The difference between this and extending a class is that when you extend a class, you can add additional functions, as well as variables. In a category, you can only add additional functions. The benefit of a category though is that you don’t have to use the extended class to get the benefits of the added additional functions. All you have to do is include the header file of the category, and the additional functions of that class are available to you.

A simple NSDate Example
Say we have an NSDate object, and we know that we want it to always be formatted in a customized way. You can create an NSDate category, with an additional function called getFormattedString, that will always return an NSString * with the current date formatted a certain way.

Starting with programming
Categories typically have the convention of ClassName+OurText.m, so we create a new file of subclass NSObject, called NSDate+FormatString.m, make sure to check “Also create NSDate+FormatString.h”. After creating those files, we need to make some minor changes to them to actually have them be treated as categories. In the header file, change
@interface NSDate_FormattedDate : NSObject { } @end
to
@interface NSDate (FormattedDate) @end
making sure to remove the brackets, in the header file. The reason for this is that a category can not add variables, only functions.
In the implementation file, change
@implementation NSDate_FormattedDtae
to
@implementation NSDate (FormattedDate)

We have now created a category! This of course doesn’t do anything yet, so lets add some meat to it.
// In NSDate+FormattedDate.h, after @interface add
- (NSString *)getFormattedString: (NSString *)desiredFormat;
// And in NSDate+FormattedDate.m, after @implementation add - (NSString *)getFormattedString: (NSString *)desiredFormat { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:desiredFormat];//@"MM/dd/yyyy h:mm a"]; NSString *returnDate = [formatter stringFromDate:self]; [formatter release]; return returnDate; }

Now to use this category, all we have to do is add
#import "NSDate+FormattedDate.h"
to any file where we have an NSDate, and we will be able to call
NSDate *today = [NSDate date]; //get the current date NSLog(@"The current date is: %@", [today getFormattedString:@"MM/dd/yyyy h:mm a"]);

This will print out today’s date all nicely formatted for you!

Of course this is just a very basic example of using a Category but it will get you started easily.
Get the sample files from github.

Update:
There is an additional step for compiling and making an universal static library which can work on both simulator and device.
Compile your library twice. Once using the device SDK, and again using the Simulator SDK.
Then use the lipo command line tool to create a "fat" library.
lipo -create libdevice.a libsimulator.a -output libcombined.a