Eliminating stringly-typed code in Objective-C

Sometimes, you just have to meet your tools halfway.

Written by Jim Puls.

As iOS developers, we can use any development environment we want, as long as it’s Xcode. Beyond the Objective-C language, Xcode gives us a bunch of really awesome features for app development, including storyboards and asset catalogs. But all of the great convenience comes with some potential headaches.

“Stringly-typed”

To reference an image set you’ve defined in an asset catalog, you use the imageNamed: method on the UIImage class, as such:

UIImage *buttonImage = [UIImage imageNamed:@"GreenButton"];

To reference a table cell you’ve defined in a storyboard, you tell the table view to dequeue one of its cells:

UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell with Switch" forIndexPath:indexPath];

In a master-detail relationship set up in a storyboard, you populate the detail controller with content when your master controller gets its prepareForSegue:sender:method called:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
{
if ([segue.identifier isEqualToString:@"Detail View"]) {
// do something useful
}
}

What happens when your designer switches out your green button for a blue one? What happens when your design changes from a cell with a switch to a cell with a button? What happens when you rename your detail view controller?

You can probably fix the problem with a find-and-replace, but if you forget, you’ll have no indication that anything is wrong until your app has a blank spot or worse, a crash. If your test coverage is lacking, it could be your customers who find the bug first.

You can mitigate the problem with comprehensive testing, but even then, you’ll only find an issue at runtime — and I can guarantee that compiling your code is faster than running your tests.

Code generation doesn’t have to suck

It’s not so hard to write a script that puts your strings in compiled code and lets you avoid a whole class of bugs. After all, asset catalogs are just nested folder structures with JSON metadata, and storyboards are a pretty readable dialect of XML.

objc-assetgen takes your asset catalogs and objc-identifierconstants takes your storyboards and outputs a couple of small source code files that turn this code:

UIImage *buttonImage = [UIImage imageNamed:@"GreenButton"];
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell with Switch" forIndexPath:indexPath];

into this code:

UIImage *buttonImage = [AppCatalog greenButtonImage];
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:AppStoryboardCellWithSwitchIdentifier forIndexPath:indexPath];

A small change, to be sure, from super-minimal extra code, so you won’t be tempted to override it or edit it. But now if you rename that table cell identifier or that button image, you’ll immediately get a build error and stop yourself from having that bug.

As a special bonus, you can use the resizable cap inset features of asset catalogs on an iOS 6 deployment target, since objc-assetgen will generate code that appropriately applies them as defined.

“Once and only once”

Fun fact: you can make custom color palettes for Mac OS X’s standard color picker, and then you can consistently use them throughout your storyboards.

But if you want to reference those same colors elsewhere, you’re stuck redefining them and hoping you’ve done it the same way.

Again, though, we can write some code to solve our problem. objc-colordump will extract the colors defined in a custom color palette into a small source code file. It turns this:

[UIColor colorWithRed:0.000f green:0.741f blue:0.643f alpha:1.000f];

into this:

[TestAppColorList tealColor];

Of course it’s open source

You can find these three tools — along with a README that more fully explains their use — on the objc-codegenutils GitHub repository. They have no external dependencies, and you can easily add them as dependencies of your app in Xcode and run them as a build phase.

This blog post is adapted from a talk I gave at a recruiting event at Carnegie Mellon University on February 4th and at Princeton University on February 6th. If you’re interested in joining us here at Square, we’re hiring! Check out the link on the right side of the page for more information.