Brian’s Brain

I Need a Tagline

Pholio Maintainability: Unit Testing, OCMock, Coding Conventions

There are no shortcuts to maintainable code. It’s a lot of work – certainly more than quickly hacking together a solution. However, I’ve found some easy and robotic things that, while insufficient on their own for making a project maintainable, certainly help a great deal: Unit testing and having a consitent coding style guide.

One excellent feature of Ruby on Rails is all of the built-in work to make it easy to test as you go. When I started Pholio, things were not nearly that easy in the iOS world. Things have come a long way in just the past 18 months. XCode has unit testing, Instruments can do UI automation testing, ARC means you can gloss over many details of memory management. But when I started Pholio, I had to discover much of this for myself.

For Pholio, I settled on using Google Toolbox for Mac for unit testing and OCMock for mock objects.

The hardest part of getting started with Google Toolbox for Mac (GTM) is just getting the right code included in your project and setting up the Unit Test project. To streamline this for other developers (and future projects of my own), I created the iOS Minimalist template. This template has GTM all set up for testing, and also uses the simplified file naming conventions described below.

Pholio Style Guide

I created my own Objective-C coding conventions smushed together from many other projects, such as:

(…as well many years of reading Windows NT kernel source code, which strictly follows what’s jokingly called “Cutler normal form” after Windows NT’s founder, David Cutler.)

Here’s the style guide that I settled on for Pholio.

  1. Spacing. Code is indented with two spaces.

    Tip: If you work on other projects that don’t follow the two-space guideline, you can set a per-project indentation settings in XCode. This is now a lifesaver for me where I use two spaces for personal projects, like Pholio, but Urbanspoon uses 4-space indentation.

    Per-project indentation settings

  2. Line length. Pholio uses a soft margin at 80 characters. If you write a line of code longer than 80 characters, wrap it if there are natural places to do so (e.g., by putting each method argument on a new line with the colons aligned). (Note: In later projects, I’ve been convinced by the Three20 style guide that 100 characters is a better soft margin for verbose Objective-C and modern monitors. In new projects I also use 100 / characters at the beginning of method definitions.)

  3. Comment line between methods. To make the division between methods even more apparent, put a single line of 80 / characters above each method, with a blank line above and below. This also helps make the soft-margin of 80 characters more apparent. For example:

     - (void)foo {
       //  Do something...
     - (void)bar {
       //  Do something else
  4. Braces. Pholio uses “walk-like-an-Egyptian” style braces: Opening braces are at the end of the line, closing braces are on their own line and at the original indentation level. There is one blank line between the opening statement and the indented code. This style is used everywhere: Methods, control-flow statements, etc. Example:

     - (void)foo:(BOOL)sample {
       if (sample) {
         //  Do something...
       } else {
         //  Do something else...
  5. File naming. There are a few standard parts of any iOS application: The application delegate, the main info.plist file, the prefix.pch file. To facilitate working on many different projects, name these files the same across all projects. Borrowing the convention from the ZDS Style Guide, Pholio calls these files AppDelegate.m/h, Info.plist, and Prefix.pch. This is different from the XCode default, which puts a project-specific name in front of each of these.

  6. Descriptive names. Use descriptive names for variables and method signatures. Names are camel-cased (e.g., topContentPadding). Because code editors autocomplete your text, there’s no penalty for using long, descriptive names, and at least one huge benefit:

  7. Comments. If you do a good job picking names, then the verbose nature of Objective-C results in quite readable and clear without comments. So, don’t repeat yourself in the comments. When you do need to write comments, format them with a blank line, a blank // line, your comment text with two spaces after the //, a final closing blank //, then a blank line. Easier to see with an example: The comments in the Braces section above are properly formatted.

  8. Use properties, not ivars. Rely on Objective-C to synthesize the ivars for you; don’t write them yourself. Never declare the ivars in your header.

  9. @synthesize. The @synthesize statements come immediately after the @implementation directive. Synthesize one property per line. The synthesized ivar name should be the same as the property name plus a trailing _, like so:

     @synthesize viewCells     = viewCells_;
     @synthesize recycledCells = recycledCells_;
     @synthesize selectedCells = selectedCells_;
  10. Use continuations to declare private properties and methods. Objective-C class continuations are awesome, so use them. Because you’re not declaring ivars in your header, and you’re not declaring anything private in your header, your header files will be a nice, clean contract between your class and the rest of the program.

  11. init and dealloc. The init and dealloc methods are always the first methods defined in the source file.

  12. Rough order of methods in a class. Through trial and error, I’ve settled on the following rough order of methods within a class:

    1) init / dealloc (important enough it gets its own rule) 2) view lifecycle methods for UIViewController subclasses 3) Custom property getter/setters 4) Other methods 5) Protocol implementations, appropriately grouped and delimited with #pragma mark - <Protocol name> statements.

    It’s so nice to have the same structure in each file…

  13. Unit tests. Unit testing is done with Google Toolbox for Mac. Each class gets its own file for unit testing, named <class>-test.m. For example, the test file for BDGridCell.m is BDGridCell-test.m. There are no .h files for unit tests. I aspire to create a unit test file each time I create a new class, even if I do nothing more than test allocating and freeing the object. The accomplishes two goals: 1) Even a simple allocate/free test can catch leaks when you later forget to release something in dealloc, and 2) If the file is in place, I’m more likely to add a quick unit test later because the cost of doing so is lower.

  14. Test controllers in isolation, and use mock objects when needed to “fake” the interaction between controllers. For example, the IPSetGridViewController has and manages a BDGridView object. This test uses mock objects to verify that the objects interact as expected:

    //  Test setting |currentSet|. Should reload the grid.
    - (void)testSetCurrentSet {
      IPSetGridViewController *controller = [[[IPSetGridViewController alloc] initWithNibName:kNibName bundle:nil] autorelease];
      IPSet *set = [IPSet setWithPageCount:5];
      id mockGrid = [OCMockObject mockForClass:[BDGridView class]];
      controller.gridView = mockGrid;
      [[mockGrid expect] reloadData];
      controller.currentSet = set;
      STAssertNoThrow([mockGrid verify], nil);
  15. TestFlight. I was late to using TestFlight, but now I can’t imagine developing a project without it. Use the TestFlight SDK to get crash reports and logs from your testers.