Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Objective-C

How To Extend Objective-C Classes With Categories

0.00/5 (No votes)
24 Nov 2009CPOL4 min read 19.2K  
How to extend objective-C classes with categories

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

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 flexibility 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 do 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

C++
#import <Foundation/Foundation.h>
@interface NSString (NSString_HTML)
@end

Implementation File

C++
#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:

C++
#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

C++
#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

C++
#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:

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

This is the complete implementation of the method:

C++
#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:

C++
#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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)