Though we want iPhone in Action to be a great introductory book, there was only so much space we could spend on the basics of Objective-C, the programming language at the heart of the iPhone SDK. We introduce protocols pretty quickly on page 179 then follow up on delegation on page 188. That's hopefully enough to get you started with writing the protocol methods for existing classes such as the TableViewController.
However, what we don't talk about in iPhone in Action is how to create your own, new protocols. In particular, delegate protocols are a pretty important element when you're creating new classes for use and reuse, so you should know how to build them.
Here's a quick step-by-step process. Generally, you'll need to take three broad steps: write the protocol; add the delegate property; and incorporate the protocol into your class.
1. Design the Protocol
As with composition of any type, the first thing you have to do is decide what you're going to do. You should have an existing reusable class that you've designed, and you should have some data that you want to send outside of the class, to the rest of your program. You need to decide what methods you want to define to export that data.
For the purposes of this article we're going to use a database class similar to the SKDatabase class shown on pp.307-308 of iPhone in Action as an example. This class can do all sorts of database work, from simple SELECTs to INSERTs, UPDATEs, and DELETEs.
One necessary protocol immediately suggests itself: it'd be nice to report out whenever a database table is updated, so that an iPhone program can update its own internal data representations. So, that's the method we're going to define in a protocol in this article.
2. Define the Protocol
In order to define a protocol, you must include @protocol information in a header file--probably the header file for the related class. It should probably go up at the top of the file, before the @interface description of your class. Here's what I wrote for my SKDatabaseDelegate protocol:
@protocol SKDatabaseDelegate <NSObject>
@optional
- (void)databaseTableWasUpdated:(NSString *)table;
@end
You'll usually write a list of methods in between a @protocol and @end statement, as shown here. There are two things of note in this example.
First, the protocol itself is given a protocol: <NSObject>. This allows it to effectively inherit protocol methods up its inheritance chain.
Second, the one method in this example is listed as @optional, which means that the delegate object doesn't have to respond to this message. Any methods which appear before such an @optional statement are considered to be required.
You're now done designing a protocol, but as of yet it's not actually linked into your class. Because we're offering an example of a delegate protocol here, the next thing you need to do is define your delegate property.
3. Create the delegate property
To create a standard delegate property you must create an instance variable and define it in your header file, then synthesize it in your .m file.
Here's the .h file info:
@interface SKDatabase : NSObject {
id<SKDatabaseDelegate> delegate;
}@property (assign) id<SKDatabaseDelegate> delegate;
The only thing of note here is the definition of the variable as of type "id<SKDatabaseDelegate>" That should be pretty typical when you're creating a delegate property: your delegate can be an object of any type, but it must follow the protocol in question.
The synthesis in the source code file is entirely standard:
@synthesize delegate;
4. Link to the Protocol
Now you're just missing one (entirely crucial) step. Your class actually needs to send a protocol message to the delegate under the appropriate circumstance. In my SKDatabase class, this means that whenever a table is updated, the delegate must be told. Here's the code that does so:
- (BOOL)runDynamicSQL:(NSString *)sql forTable:(NSString *)table {
// Actual Code to run SQL goes here
if (result) {
if (self.delegate != NULL && [self.delegate respondsToSelector:@selector(databaseTableWasUpdated:)]) {
[delegate databaseTableWasUpdated:table];
}
return YES;
}}
Ignoring all the SQLite3 code for the moment, you can see that the delegate code does three things.
- It makes sure that a delegate has been defined.
- It makes sure that the delegate responds to the selector in question--which is important since this was an optional protocol method. For a required method, you might actually just let the program crash, to help alert the programmer to his bug.
- It tells the delegate to run the method.
5. Use Your Class
With that code all in hand, you can now use your class just like you'd use any protocol already defined by Apple.
In the case of my database, I'm currently using it in a program that draws Table Views and UIViews based on database contents. Thus, it's really handy to know when a database table is updated, and thus when I should redraw the pages in question.
I'll be sharing the actual class, as promised, this week or next, once I finish polishing everything up.

Instead of crashing with required delegate, would it not be better to log it using NSLog or similar methods?
Ries
Posted by: Ries van twisk | July 19, 2009 at 05:26 PM
Not included a required delegate is fundamentally a programming error, not a runtime error, so I'm not convinced you need to be very polite about it. However, including an NSLog (before you error out) might help a programmer with debugging, so if you're making something for more public consumption, I don't see why not.
Posted by: Shannon Appelcline | July 20, 2009 at 10:38 AM
No need to check self.delegate for null before calling -respondsToSelector:
Messages to nil do not cause errors in Cocoa. The resulting code is cleaner, and more idiomatic.
Posted by: Paul Franceus | July 22, 2009 at 02:51 PM
Nice article. But what will happen if I try to subclass an object (i.e. UIScrollView) that already has its own delegate? How can I make sure that the subclass' delegate will still contain its parent class' delegate?
Posted by: mariot | August 12, 2009 at 07:57 AM
great article. good luck for all
Posted by: iphone trend | February 26, 2010 at 08:24 AM