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 string
s, 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
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
#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!