iOS – Updating application state from a UITableViewCell

One of my current clients is a new restaurant where they want an iOS app from which their customers can order their food.  One of the requirements is allowing the user to select 0-n number of dishes for multiple dishes.  For example:

Side dishes

So the question is as the user is incrementing the values, where do we store this state?  The first thought is to just keep it in the UITableViewCell but this has major drawbacks, including:

  • the data is lost once the UITableViewCell is scrolled off screen, as it is re-used to display other dishes.
  • maintaining state in the view is just not clean design.  This is controller territory.

Protocols and Delegation

The sensible place to keep the data is in the controller.  Then the question becomes, “How do we call back to the controller from the UITableViewCell?”  

Using Protocols and Delegation, we can easily have the controller maintain the state.  Here’s I did this.

SideDishTableViewCell -> UITableViewCell

I subclassed the view cell with SideDishTableViewCell (SDTVC).  In SDTVC, I defined a protocol:

// SideDishTableViewCell.h

@protocol SideDishTableViewCellDelegate;
@interface SideDishTableViewCell : UITableViewCell

// define a @property to hold a reference to the delegate
@property (assign, nonatomic) id <SideDishTableViewCellDelegate> delegate;
@end

// define the @protocol here
@protocol SideDishTableViewCellDelegate <NSObject>
@optional
- (int)addItemWithCell:(SideDishTableViewCell *)cell;
- (int)removeItemWithCell:(SideDishTableViewCell *)cell;
@end

Now the SDTVC has a reference to a delegate which we’ll call whenever the user adds or removes items.  One note: I chose to pass the entire SDTVC because it contains bits of data that the controller needs. Another option is to just pass the dish’s ID and have the controller do a bit more work to get its metadata.

Calling the delegate from IBAction

Each of the + and – buttons are tied to IBActions, and the IBAction methods are where we call out to the delegate.

// SideDishTableViewCell.m

// The value 'q' is returned from the controller and is used to update the quantity displayed.  The View does no math.
- (IBAction)increaseQuantity:(id)sender {
    int q = [[self delegate] addItemWithCell:self];
    self.quantityLabel.text = [NSString stringWithFormat:@"%d", q];
}
- (IBAction)decreaseQuantity:(id)sender {
    int q = [[self delegate] removeItemWithCell:self];
    self.quantityLabel.text = [NSString stringWithFormat:@"%d", q];
}

Implementing the protocol from the View Controller

// SideDishViewController.h

#import "SideDishTableViewCell.h"
@interface SideDishViewController : UIViewController <SideDishTableViewCellDelegate>

And the implementation.

// SideDishViewController.m
-(int)addItemWithCell:(SideDishTableViewCell *)cell {
     // Update a collection that holds the dishes and their quantities

     // Return this side dish's quantity
}

Summary

There you have it.  We’ve kept the view very simple to the point it doesn’t even have to do any math.  It simply lets the controller handle the increment/decrement and then just waits for the controller to tell it what the new quantity is.

 

Getting hopscotch.fm to version 1.0

This week I launched hopscotch.fm.  It is still a very early stage site and there are many things I’d like to add on and improve, but I decided I needed to draw a line in the sand and just ship something.  Anything.

In this blog post I want to give a general sense of how I went from idea to v1.0 going live.

Step 1: Proof of Concept

In this phase I wanted to rule out any potential major unknowns.  The two biggest ones were:

  • could I use Songkick’s API for my show listings
  • could I build a radio player around Youtube videos

The first one was answered fairly quickly.  The Songkick API features loads of shows and venues and while it’s not the cleanest data (dates are not always proper), it is a great start.

The second was a bit more involved.  I want to make hopscotch.fm a radio player so that when a song completes, it automatically moves on to the next song without the user doing anything.  I found a great library tubeplayer.js that did this.

Step 2: Get an ugly site up and running

It doesn’t have to be ugly, but I wasted no time in making anything look good.  I had buttons all over the page, images showing up over controls and other oddness.  But I got the functionality implemented.  The user hits play and:

  • The Youtube video starts playing
  • An artist image is downloaded via Youtube’s API
  • Venue info is retrieved from Songkick
  • Artist info is retrieved from Wikipedia.  I since removed this as it was the wrong half the time.

Step 3: Decide what constitutes v1.0 and do it

It’s probably no surprise that this step took the most time as now I needed to start caring about error handling and CSS and data validation and everything else that makes a real site, real.  I added Bootstrap, built a little admin page for syncing hopscotch data with Songkick.  Made a best guess effort at what a clean, simple user experience could be and from feedback I’ve gotten, I think it’s a decent start.

Step 4: Hosting and stuff

I originally put it on heroku as they have a free starter level.  But the 20 second startup time just to get the page to load is ridiculous so I’ve now switched to nodejitsu.  Data is on mongolab.  It’s all in the cloud.  It’s all happy.

Step 5: Go see a show!

That’s what hopscotch.fm is for so why not?