Anyone who has developed in Cocoa for any length of time will have, perhaps unbeknownst to themselves, encountered the Delegation pattern. UITableView, for example, exposes a wide range of delegate methods but hands responsibility for implementation over to it’s parent ViewController which is assigned as the delegate of the UITableView.
While this concept is not too difficult to comprehend, designing custom class hierarchies that implement the Delegation pattern can be quite a different proposition and can be confusing without an example. This tutorial hopes to help with that.
As an example, let’s consider the contestants in a quiz. Each contestant can answer or pass on a question whilst the quiz can accept responses of both types from it’s contestants and manage things accordingly. This should achieve good object orientation and should delegate the work to the correct classes using the Delegation pattern. Here’s our contestant:
@interface Contestant : NSObject {
id delegate;
NSString *name;
BOOL active;
}
@property (nonatomic, retain) id delegate;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) BOOL active;
-(id)initWithContestantName:(NSString *)contestantName;
-(void)answerQuestion:(NSString *)answerAttempt;
-(void)pass;
@end
After the @end we specify the ContestantDelegate protocol that objects should implement if they wish to be delegates for Contestant objects:
@protocol ContestantDelegate
-(void)contestantAnswers:(Contestant *)contestant Answer:(NSString *)contestantAnswer;
-(void)contestantPasses:(Contestant *)contestant;
@end
@implementation Contestant
@synthesize delegate;
@synthesize name;
@synthesize active;
-(id)initWithContestantName:(NSString *)contestantName {
self.name = contestantName;
self.active = YES;
return self;
}
-(void)answerQuestion:(NSString *)answerAttempt {
if(self.delegate && [self.delegate respondsToSelector:@selector(contestantAnswers:Answer:)]) {
[self.delegate contestantAnswers:self Answer:answerAttempt];
}
}
-(void)pass {
if(self.delegate && [self.delegate respondsToSelector:@selector(contestantPasses:)]) {
[self.delegate contestantPasses:self];
}
}
-(void)dealloc {
[super dealloc];
}
@end
So now we have a Contestant who can answer a question and can also pass on a question. They can have a name and also they can be active or not, a status which can be used in the quiz.
We have a delegate protocol that tells any class which implements it that it should be able to handle an event when a contestant answers or passes. This wraps up our Contestant and any information it needs to give to delegate classes.
Next we need to create an object that implements our delegate protocol so that it can manage the quiz and it’s contestants, unsurprisingly it’s called Quiz:
@interface Quiz : NSObject <ContestantDelegate> {
NSMutableArray *contestants;
}
@property (nonatomic, retain) NSMutableArray *contestants;
-(void)addContestant:(Contestant*)newContestant;
@end
@implementation Quiz
@synthesize contestants;
-(id)init {
self.contestants = [[NSMutableArray alloc] init];
return self;
}
-(void)addContestant:(Contestant *)newContestant {
newContestant.delegate = self;
[self.contestants addObject:newContestant];
}
-(void)contestantAnswers:(Contestant *)contestant Answer:(NSString *)contestantAnswer {
NSString *correctAnswer = @”CorrectAnswer”;
if([contestantAnswer compare:correctAnswer] != 0) {
[contestant setActive:NO];
}
}
-(void)contestantPasses:(Contestant *)contestant {
[contestant setActive:NO];
}
-(void)dealloc {
[contestants release];
[super dealloc];
}
@end
As you can see in the Quiz class, it implements the methods from our ContestantDelegate protocol and also has a method of it’s own for adding contestants to the quiz. The contestant adding method looks after setting the delegate of the Contestant to the Quiz, so the viewcontroller has one less task to worry about. The implementations are simple enough to avoid confusing the tutorial, obviously your answering methods will be a bit more complex.
With our Quiz and Contestant classes complete, all that’s left to do is implement a View with a ViewController to run the quiz for us. All the necessary files are included with this tutorial in a zip file. The view controller will need a Quiz member variable that looks after the running of the quiz. You can set the Quiz and it’s member contestants up in the viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.quiz = [[Quiz alloc] init];
Contestant *contestantOne = [[Contestant alloc] initWithContestantName:@”Player 1″];
Contestant *contestantTwo = [[Contestant alloc] initWithContestantName:@”Player 2″];
[self.quiz addContestant:contestantOne];
[self.quiz addContestant:contestantTwo];
[self updatePlayersRemainingInfo];
}
After that, it’s really up to yourself how the Quiz interacts with the user. In the attached ZIP the quiz has two contestants and buttons for each to answer or pass, ultimately you’d probably extend the example to have a current active contestant and only one button for answering and passing. Since that doesn’t really add much to understanding delegation, I’ve left it out. One simple example to just clarify the point:
-(IBAction)playerOnePressedAnswer:(id)sender {
[self.answerInputField resignFirstResponder];
Contestant *playerOne = (Contestant*)[self.quiz.contestants objectAtIndex:0];
[playerOne answerQuestion:[self.answerInputField text]];
[self updatePlayersRemainingInfo];
}
When you press the Player One Answer button in our example, this fires the answerQuestion method on the Contestant class. After that, you leave everything to the delegate pattern to get the information out to the right classes. We call a helper method at the end to update our pretty basic front end.
Using the example application you can download here, you can run a pretty rudimentary quiz where the right answer is always “CorrectAnswer”. When you press the buttons, the application will fire the methods of your Contestant objects and you will see that control gets passed to the Quiz object delegate. This allows the Quiz to manage answer validation or pass acceptance and deactivate Contestants when they get something wrong or pass on a question. The example application will notify you that the game is over when all Contestants are deactivated.
Ultimately, you could implement changes that react to one user being deactivated that declares the other user the winner or reacts to a correct answer by showing a message. Perhaps QuizViewController could implement a new QuizDelegate?
Sample App: DelegationExample.zip