Search

Protocols Part 3 : Adopting Protocols in Class Extensions

Mark Dalrymple

3 min read

Apr 15, 2012

Protocols Part 3 : Adopting Protocols in Class Extensions

One of the directions Apple is taking in Objective C that I’ve come to really like is the migration of stuff out of header files. I’m a firm believer that header files should only contain the public programming interface, along with any bookkeeping the compiler absolutely has to have And nothing else. Anything that doesn’t contribute to a person’s understanding of how to use your class shouldn’t be in there.

When I first started programming iOS I didn’t like the explicit protocols for data sources and delegates. I could understand why Apple does it, to make it explicit that “Hey! I know what I’m doing when I say I’m going to be a data source.” Things are much more explicit than what you get with an informal protocol, which is just a category. But it made the header feel kind of junky.

Say that one of my teammates on the BigLunch team wrote a view controller that lets the user choose a sandwich. It has a table view for picking the sandwich type (grinder, sub, hero, torpedo) and a spinner-picker for sandwich fillings (tofurkey, spam). The header file would look like this:

// BNRSandwichChooser.h
@interface BNRSandwichChooser : UIViewController <UITableViewDataSource,
                                                  UITableViewDelegate,
                                                  UIPickerViewDataSource,
                                                  UIPickerViewDelegate>
- (BNRSandwich *) chooseLunch;
@end // BNRSandwichChooser

The table view and picker control are just implementation details. I, as a programming using this view controller, don’t care that it uses these particular UI objects. I just want to let the user choose lunch, and then go and do other stuff with their lunch choice. But, the BNRSandwichChooser needs to adopt the protocols somewhere so that the compiler won’t complain when setting the delegate:

- (void) viewDidLoad {
    self.tableView.datasource = self;
} // viewDidLoad

If the lunch chooser implementation changes to a text field or a web view, I don’t want my code to have to be recompiled because the header changed.

When Apple added class extensions, that bit of syntax that looks like a nameless category declaration, they also added the ability to to conform to protocols there:

// BNRSandwichChooser.m
#import "BNRSandwichChooser.h"

@interface BNRSandwichChooser () <UITableViewDatasource,
                                  UIPickerViewDataSource, ...>
@end
@implementation BNRSandwichChooser
...
@end

Leaving the interface nice and minimal:

@interface BNRSandwichChooser : UIViewController
- (BNRSandwich *) chooseLunch;
@end

Fixing the Map

If you remember last time, I showed a world map that used a delegate to get information from another class, and also to inform it that stuff happened. Here is what the header looked like:

#import <Cocoa/Cocoa.h>

<b>#import "BNRWorldMapView.h"</b>
@interface BNRAppController : NSObject <NSApplicationDelegate,
                                        <b>BNRWorldMapViewDelegate</b>>
@property (unsafe_unretained) IBOutlet NSWindow *window;
@property (weak) IBOutlet BNRWorldMapView *worldMap;
@end // BNRAppController

It is kind of junky, having to include the map header (forcing a recompile of BNRAppController if you change a comment in BNRWorldMapView.h), and adopt the protocol. To clean up the header, remove the import and the adoption, and add a @class to forward-declare the world map view so Interface Builder will know what type to target a connection to;

// BNRAppController.h
#import <Cocoa/Cocoa.h>

@class BNRWorldMapView;
@interface BNRAppController : NSObject <NSApplicationDelegate>
@property (unsafe_unretained) IBOutlet NSWindow *window;
@property (weak) IBOutlet BNRWorldMapView *worldMap;
@end // BNRAppController

And then in the implementation, I’d add the adoption to the class extension:

// BNRAppController.m
@interface BNRAppController () <b><BNRWorldMapViewDelegate></b>
@property  NSMutableSet *selectedCountries;
@end // BNRAppController

The takeaway? Unless the protocol adoption has to be public for other classes to compile correctly, I now put all implementation-detail protocol adoptions into a class extension in the implementation file. This keeps the implementation details nice and hidden, and keeps the interface clean.

Mark Dalrymple

Author Big Nerd Ranch

MarkD is a long-time Unix and Mac developer, having worked at AOL, Google, and several start-ups over the years.  He’s the author of Advanced Mac OS X Programming: The Big Nerd Ranch Guide, over 100 blog posts for Big Nerd Ranch, and an occasional speaker at conferences. Believing in the power of community, he’s a co-founder of CocoaHeads, an international Mac and iPhone meetup, and runs the Pittsburgh PA chapter. In his spare time, he plays orchestral and swing band music.

Speak with a Nerd

Schedule a call today! Our team of Nerds are ready to help

Let's Talk

Related Posts

We are ready to discuss your needs.

Not applicable? Click here to schedule a call.

Stay in Touch WITH Big Nerd Ranch News