Oct 30, 2008

Police nab bicycle thief thanks to stupidity.

This is a little different type of post for me, but last week I had three bicycles stolen from my locked storage unit at my apartment building here in San Francisco. Thanks to some quick thinking by one of Stacie's employees and a bit of finagling on my part, we were able to arrange for the "alleged" thief to get arrested and for me to recover at least one of the bikes. Instead of reposting the whole story I'd like to refer you to my daughter's blog: http://ilovelladro.blogspot.com/2008/10/sting-at-google.html

Oct 12, 2008

Organizing a complex UITableView with the iPhone SDK by using a dispatch table

Hi. I've been working on an iPhone SDK application for a while, and one major piece of this application is a fairly complex table view. The UITableView is the workhorse class of most iPhone applications - you see it everywhere. One problem with the table view is that the controller class tends to get very large and complex, especially if you have a multiple section table view that has different types of custom cells and behaviors. There are many things that can be done to make this job easier. One excellent technique was recently documented by Frasier Speirs: A technique for using UITableView and retaining your sanity.

I've developed a technique that can make use of this technique but that goes beyond it by allowing you to delegate the handling of each section in a table view to its own class. It is also easily configurable if you need to add or remove a section type from your controller later, and allows you to localize your changes so that you don't have to remember to modify code in several places.


As I'm sure many of you know, the UITableViewDelegate protocol contains about 16 methods that take either an NSIndexPath object that specifies a row in a specific section, or an NSInteger that specifies a section.

The most obvious way to implement this would be through a switch statement in each of these methods. In the case of my program, this rapidly became unworkable, since I have 5 different sections, some with their own custom view types and specific logic for each section that uses a different portion of the data model and logic to generate the table view.


The basis of my technique is to create a main view controller for the table view, in this case called NoteViewController, and separate section controllers for each section that inherit from an AbstractNoteSectionController class that I created. This class defines the interface for the section controllers and provides reasonable default behavior in case I don't need specific behavior in my subclasses. The interface for this class is:

@interface AbstractNoteSectionController: NSObject {
NSUInteger section;
NoteViewController *parent;
}

@property NSUInteger section;
@property (nonatomic,retain) NoteViewController *parent;

- (id)initWithNoteViewController:(NoteViewController *)cnt
                      andSection:(NSUInteger)aSection;

- (NSUInteger)numberOfRows;

- (UITableViewCell *)cellForRow:(NSUInteger)row;

- (UITableViewCellEditingStyle)editingStyleForRow:
   (NSUInteger)row;

- (void)selectedRow:(NSUInteger)row
    withEditingStyle:(UITableViewCellEditingStyle)style;

- (void)commitEditingStyle:(UITableViewCellEditingStyle)style
                    forRow:(NSUInteger)row;

- (BOOL)canEditRow:(NSUInteger)row;

- (BOOL)shouldIndentWhileEditingRow:(NSUInteger)row;

@end

I've only implemented the table view delegate methods that I need for my application. I then subclass this and implement the specific functionality for each section in a separate subclass. In the main NoteViewController class, I have a sectionDict instance variable that has an NSDictionary object keyed by the section number:

// Build a dispatch table so we can more
// easily navigate all the sections.
- (NSMutableDictionary *)buildSectionDict {
  return [NSMutableDictionary dictionaryWithObjectsAndKeys:
    [[[FirstSectionController alloc]
      initWithNoteViewController:self
                      andSection:FIRST_SECTION] autorelease], 
    [NSNumber numberWithUnsignedInteger:FIRST_SECTION],
...
    [[[LastSectionController alloc]
      initWithNoteViewController:self
                      andSection:LAST_SECTION] autorelease],
    [NSNumber numberWithUnsignedInteger:LAST_SECTION], nil];
}

Then, I have a method to select the section controller given a section:
- (AbstractNoteSectionController *)
    sectionControllerForSection:(NSUInteger)aSection {
  NSNumber *section =
    [NSNumber numberWithUnsignedInteger:aSection];
  return (AbstractNoteSectionController *)
    [sectionDict objectForKey:section];
}
After this, something like tableView:numberOfRowsInSection: is reduced to this simple piece of code from a fairly complex and difficult to maintain switch statement:

- (NSInteger)tableView:(UITableView *)tableView
  numberOfRowsInSection:(NSInteger)section {
  return [[self sectionControllerForSection:section]
    numberOfRows];
}

This allows each section controller to be focused and readable. For example, the numberOfRows method for the PhotosSectionController is very simple.

- (NSUInteger)numberOfRows {
  return ((self.cameraAvailable) ? 2 : 1) +
      [parent.note countOfPhotos];
}

I suppose that I should mention at this point that each section controller has a parent member that points to the NoteViewController which still mediates access to the model (parent.note)


I love programming with this type of dispatch table. It makes for extremely flexible code that is not at all brittle, because I can add or remove sections from the table view by simply editing the buildSectionDict method and writing a new subclass of AbstractNoteSectionController.  No other methods of the NoteViewController ever have to change, which is great because it gives me fewer things to think about.

Using this technique, I was able to split up a hard to read and manage 700 line behemoth class into much more managable 80-200 line pieces and improve the readability and flexibilty of my code immensely. Hope you find this useful in your iPhone development.