How To Extend Objective-C Classes With Categories

Post image for How To Extend Objective-C Classes With Categories

by mattjdrake on November 23, 2009

Here is how to extend the functionality of existing classes like NSString and NSURL using categories. This is a real clean way to extend and customize objects for your app without creating sub-classes. This makes categories a nice way to get some flexibilty and code reuse.

NSString to HTMLString

For the demo I am going to create a make-shift HTML editor by extending the NSString classes with methods that enclose text inside of HTML tags. The app will do this and display the results in an UIWebView.

Here is a quick overview of what the app will do:

Why Use Categories?

Categories are a clean way of adding behavior that belongs to a class but only in a specific implementation. You can use a class to add special methods just for one region in your app that does not belong everywhere else. In the example I am using I want to be able to quickly add HTML tags to certain strings, but this is not something that generally belongs as a function of NSString.

So my app needs HTML support and it logically belongs in NSString for this purpose. But, I don’t want all my apps to have this option nor do I want to have to use a special sub-class of NSString throughout this component (because I will never remember to do this later on). With categories I can define my own behavior for a class and then selectively apply this behavior to classes in my apps when needed.

Implement HTML Writer With Categories

Now, let’s implement the HTML Writer App Using Categories.

Buttons, Views, TextView and WebViews Oh My!

HTML Writer Screen

HTML Writer Screen

As a side note I simply used Interface Builder to lay this UI out and hook the components up to the respective IBOutlets and IBActions. Something I so also is get each UIButton’s tag property so I can identify it later on in code. This will be used to identify what tag should be wrapped around the text.

Extending NSString With HTML

To start extending NSString all you need to do is to add interface and implementation files and add this code:

Interface File

	#import <Foundation/Foundation.h>

	@interface NSString (NSString_HTML)

	@end

Implementation File

	#import "NSString_HTML.h"

	@implementation NSString (NSString_HTML)

	@end

This looks a bit like a typical class definition but it is different. Here after the interface you will see the name of the class that you want to extend (NSString) followed by the name of the category (NSString_HTML). Other than that this code should look familiar.

BTW: I could have simply included this interface and implementation directly in the file where I want to use the category. However, since I may want to reuse this code in the future I felt that it makes more sense to keep it in another file.

Add #define to Represent HTML Tags

To make things easier on myself I am going to define some integers that will serve as the HTML tags that I am supporting: p, h1, h2, i and u. I will do this in the interface:

	#import <Foundation/Foundation.h>

	#define P	0
	#define H1	1
	#define H2	2
	#define U	3
	#define I	4

	@interface NSString (NSString_HTML)

	@end

Define Method

This is where we add the extended functionality to NSString. I am going to add a method to the category just like I would for a regular class:

Interface

	#import <Foundation/Foundation.h>

	#define P	0
	#define H1	1
	#define H2	2
	#define U	3
	#define I	4

	@interface NSString (NSString_HTML)

	-(NSString *) stringWithThisTag:(int)tag;

	@end

Implementation

	#import "NSString_HTML.h"

	@implementation NSString (NSString_HTML)

	-(NSString *) stringWithThisTag:(int)tag{

	}

	@end

Now that I have everything in place all I need to do is the actual implementation of the method. I am simply going to enclose the string based on the integer tag that is passed as a parameter to the method. I will also use the self keyword to identify the string itself. Here is an example of the general pattern that I am following:

	return [NSString stringWithFormat:@"<tag>%@</tag>",self];

This is the complete implementation of the method:

	#import "NSString_HTML.h"

	@implementation NSString (NSString_HTML)

	-(NSString *) stringWithThisTag:(int)tag{
		switch (tag) {
			case P:
				return [NSString stringWithFormat:@"<p>%@</p>",self];
				break;
			case H1:
				return [NSString stringWithFormat:@"<h1>%@</h1>",self];
				break;
			case H2:
				return [NSString stringWithFormat:@"<h2>%@</h2>",self];
				break;
			case U:
				return [NSString stringWithFormat:@"<u>%@</u>",self];
				break;
			case I:
				return [NSString stringWithFormat:@"<i>%@</i>",self];
				break;
			default:
				return self;
				break;
		}
	}

	@end

Now We Can Use This Method Anywhere!

Now that we have defined the method to add tag support to NSString we can use it anywhere in our project. All we need to do is import the category and use the method. Here is how I did this in my view controller:

	#import "HTMLWriterViewController.h"
	#import "NSString_HTML.h"

	@implementation HTMLWriterViewController
	@synthesize textView, webView;

	-(IBAction) addTag:(id)sender{
		UIButton *button = sender;
		textView.text = [textView.text stringWithThisTag:button.tag];
	...

I did omit some of the code here for clarity. Essentially, at the top I imported NSString_HTML.h which makes the stringWithThisTag method available to me. Then I use the new function at the end where I pass the tag property of the button that fired the event as a parameter (each UIButton was tagged with an integer that corresponds to the integer that my new function needs to figure out which tag to use).

See It All Come Together In This Screen Cast

Check out this video tour of all this code in action as well as some other tweaks that I did with NSURL:

What Behavior Would Be Fun to Add to a Foundation Class?

Discuss in the comments below!

Please share this if you like it!
  • Digg
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • email
  • FriendFeed
  • LinkedIn
  • MySpace
  • Ping.fm
  • StumbleUpon
  • Suggest to Techmeme via Twitter
  • Technorati
  • Tumblr
  • Yahoo! Bookmarks

{ 4 comments… read them below or add one }

1 hawk November 23, 2009 at 3:25 pm

This post came in very handy today. Why? Well because I finished reading about Categories in the Cocoa Design Patterns. So it was since to see it in action. Thanks again!

2 mattjdrake November 23, 2009 at 3:47 pm

@hawk – awesome I’m glad that you liked it.

BTW: Did you find Cocoa Design Patterns to be a good read? That one is on my list and I have a feeling that it would be really helpful…

3 webXL November 24, 2009 at 3:24 pm

Nice tutorial, but I’m not sure I understand the differences between subclassing and categories. Are categories used just so you don’t have to instantiate a subclassed object to get added functionality? Or is it required in this instance because the text component can only work with an NSString?

4 mattjdrake November 24, 2009 at 4:12 pm

@webXL – you can use sub-classing and categories for similar problems and the difference is pretty subtle in a lot of ways. It is not really required and you could do a similar thing if you sub-classed NSString and used that object in place of a NSString object.

Essentially though, the main thing is that when you are using categories you don’t need to worry about replacing your NSString objects with a subclassed version of NSString.

This way, when you already have objects in play (such as the text property of my UITextView) you can simply import the category and you have the functionality available to you automatically.

Previous post:

Next post: