George MacKerron: code blog

GIS, software development, and other snippets

No more busywork! DRY up your NSString constants

Preamble

In the last few years, Objective-C has become enormously DRYer. For example: in the past, adding a property to a class meant adding several of: an ivar, a @property, a @synthesize, a getter/setter, and a release call in dealloc. Worse, renaming or deleting the property meant updating all these. This was error-prone busywork, and made iOS development frankly pretty tedious.

Nowadays a single @property is often all that’s needed. Plus of course we’ve lost all that retain / release / autorelease noise, and got fast enumeration, literals for NSNumber, NSArray, NSDictionary and so on. This is great (and is one reason why I’ve come back to Xcode after a brief dalliance with RubyMotion).

Anyway, I really do hate repeating myself, and one of the more annoying remaining areas where this has been necessary has been string constants. These are pretty widely used in Cocoa for NSNotification names, dictionary keys, and so on.

Problem

In the past I’ve used #define in my headers. For example,

#define TapNotification @"TapNotification"

This seems like fairly innocuous repetition, but it’s still annoying. And the idiomatic/Apple way is worse. You do this in the header file:

extern NSString* const TapNotification;

Backed up by this in the implementation file:

NSString* const TapNotification = @"TapNotification";

We type the same identifier (and its accompanying line noise) no fewer than THREE times.

Solution

The best solution I’ve found involves a macro plus a one-line sed command.

The macro goes like this:

#define StrConst(s) extern NSString* const s;

And the one-line sed command (added as a build phase straight after ‘Target Dependencies’) goes like this:

sed -nE 's/[[:<:]]StrConst[[:space:]]*\([[:space:]]*([^)[:space:]]+)[[:space:]]*\)/NSString* const \1 = @"\1";/pg' ${SOURCE_ROOT}/${PROJECT_NAME}/*.h > ${SOURCE_ROOT}/${PROJECT_NAME}/AutoStrConsts.m

Now all you do for a string constant is type this in your header file:

StrConst(TapNotification)

The macro converts this to the right form for the header, and the sed command searches all your headers to create a single implementation file that contains all the corresponding definitions (you’ll need to manually add this file to the project when it’s first created).

If you think this is too much magic, I’ll understand. But for me, it’s a necessary battle in the war on busywork.

Share

Written by George

July 2nd, 2014 at 10:13 am

Posted in iPhone,Mac