Brain farts

What happened?

In spite of plenty of advance warning from Twitter, we got caught by the Twitpocalypse bug.

For the 2.0.1 release, we had tested our software extensively. I actually wrote an emulation layer on top of the code that reads data from Twitter that added a large number to every ID read from Twitter. This testing uncovered several bugs which were fixed and incorporated into the MGTwitterEngine open source code.

Unfortunately, this testing didn’t take into account that the library we use to parse the data (YAJL) includes a range check that ensures signed values, which are allowed by the JSON specification, will fit into 32-bit storage. The “YAJL Error 3” is that library telling us that the range check failed.

In hindsight, I should have fed the parser some large unsigned integers. In the wonderful world of software development, we call this a brain fart.

How did we respond?

Unfortunately, the Twitpocalypse occurred on Friday evening, just as my wife and I were heading out for a high school graduation. I quickly shot off an email to Twitter asking a few questions (I had mistakenly thought that there were problems with data being returned by an authenticated connection.)

After a couple of hours of iPhone email and SMS, it was clear that we were going to need do a new release. We had a critical bug that affected thousands of users. And our fear was this submission would take longer than normal: many developers are submitting 3.0 updates.

Life officially sucked at this point, and the graduation ceremony was memorable for all the wrong reasons.

After getting a few hours of fitful rest, I opened up Xcode on Saturday morning and started looking for the problem. It took just a few minutes to find the bug and another few minutes to fix it. Our concern at this point was getting the update to users.

Apple saves the day

In spite of it being the middle of weekend after a busy week at WWDC, we were able to get in touch with Apple Developer Relations. Our contact was able to expedite the approval of the application.

To say that this was a relief would be the understatement of the century. The update for the free version started showing up in iTunes on Sunday evening. The paid version was updated on Tuesday morning.

What can we learn from this?

Developers are human. We make mistakes. It’s interesting to note that Loren Brichter and Buzz Anderson, fellow developers whose skills I hold in high regard, were also affected by the Twitpocalypse. It really does happen to the best of us.

It’s also interesting to see that Loren and Buzz reacted in the same way I did: by fixing the problem ASAP. In Loren’s case, that meant finding a hotel with WiFi in order to distribute his update. Buzz released a new beta in a matter of hours.

In my experience, these brain farts are problems with easy fixes. It’s something like checking for invalid bounds, getting a Boolean state wrong, or something else of that nature. It’s not a complex problem: it’s an oversight.

The problem, therefore, is not how fast we can react to fixing critical bugs, but how fast the App Store reviewers can react with an approval.

As a device that’s constantly connected to the Internet, the iPhone taps into a stream of data that is unpredictable. Data in “the cloud” can change at any time, and web applications naturally adapt to these changes with a quick deployment strategies. Those of us who are building client applications on top of this network infrastructure need the ability to adapt quickly, too.

Security issues are another area where a quick response is a requirement, not a luxury. If I discover something that puts a user’s private data at risk, it’s my responsibility to fix the problem as quickly as possible. Time spent in a review queue is time spent being exposed to a flaw.

At the same time, we shouldn’t blame Apple for these delays in reviewing applications. In the year that they have been selling our products, the App Store has been more successful than anyone imagined. It’s clear to me that reviewers and others involved in the approval process are overwhelmed by this success.

How can we fix it?

Fortunately, I think there’s a simple way to solve this problem for all developers selling products on the App Store. The inspiration for this solution will be obvious to anyone who’s used Apple’s Developer Technical Support (DTS.)

When you purchase an ADC membership, you are given a number of “incidents”. These DTS incidents can be used when you have a problem that can’t be solved through documentation, support forums or hours and hours of debugging. It’s for the hard stuff, and usually involves getting an engineer at Apple involved to understand and fix the issue.

As a developer, I’m very careful to use these incidents wisely: they are a last resort. There are some years where I don’t use them at all, those are the good years.

A similar system could be put in place for critical bug fixes on the App Store. If every developer was given one or two “prioritized reviews,” it would act as insurance for the brain farts. You’d have a way to raise a flag and say “I need special attention for a critical bug.”

If another developer has a critical bug, I have no problem with my review process for a feature release taking a little longer. And since prioritized reviews would be a scarce resource, they won’t be open for abuse because developers will think twice before using them.

Because it’s not a matter of if you have a brain fart that leads to a critical bug, it’s a matter of when.

Of toolbars and actions

Another area where I find iPhone development to be a bit convoluted was with toolbars and action sheets. The sheets are conceptually tied to the toolbar, yet there is no glue to combine UIActionSheet with UIToolbar. It’s also fairly difficult to represent your application state in the toolbar—an example is the refresh button in Twitterrific that turns into a cancel button while the refresh is taking place.

And then one day it dawned on me: an iPhone toolbar with an action sheet is like a menubar with menus on the Mac desktop (turn your phone upside down and there is even some visual similarity.)

After investigating some of the design patterns for NSMenu and NSMenuItem, it was clear that this would work well on a mobile interface as well. My implementation is a subclass of UIToolbar named IFActionToolbar.

In it’s simplest form, you attach a list of items to one of the toolbar’s bar button items:

@interface MyViewController : UIViewController
{
  IBOutlet IFActionToolbar *toolbar;
  IBOutlet UIBarButtonItem *barButtonItem;
}

...
@implementation MyViewController  

...

- (void)viewDidLoad
{
  [super viewDidLoad];

  IFActionToolbarActionItem *actionItem = nil;

  NSMutableArray *actionItems = [NSMutableArray arrayWithCapacity:3];
  actionItem = [[[IFActionToolbarActionItem alloc] initWithTitle:@"One" target:self action:@selector(firstAction:)] autorelease];
  [actionItems addObject:actionItem];
  actionItem = [[[IFActionToolbarActionItem alloc] initWithTitle:@"Two" target:self action:@selector(secondAction:)] autorelease];
  [actionItems addObject:actionItem];
  actionItem = [[[IFActionToolbarActionItem alloc] initWithTitle:@"Cancel" target:nil action:NULL] autorelease];
  [actionItems addObject:actionItem];
  [toolbar attachActionItems:actionItems toItem:barButtonItem];

  [toolbar update];
}

The IFActionToolbarActionItem is just a simple wrapper object that associates a title with a target and action. These action items are used to automatically construct the action sheet when the user taps on barButtonItem. If you’ve ever constructed an NSMenu using NSMenuItems, this will feel quite familiar.

Additionally, the toolbar supports an -update method. This method calls the assigned delegate to enable or disable toolbar buttons, set the image for the toolbar item, change the title of the action sheet, hide or show items in the action sheet, and define the cancel or destructive buttons in the sheet. Again, similar in pattern to how NSMenuItems are maintained.

Any time you call this update method, the UIToolbar is configured automatically. It makes it much easier to reflect the state of your controller in the view.

I’m not going to spend too much time discussing the inner workings of this class: the picture of a sample project is worth a thousand words.

One thing I would like to caution you about: don’t get too carried away with enabling and disabling items in the action menu. In our UI testing, we found that changing the list too much was confusing for users. It’s like the “adaptive menus” used in Microsoft Office: it’s very difficult for a user to adapt to your interface if it’s constantly changing. Just because you can doesn’t mean you should.

Again, I hope this code is useful in your own iPhone projects. Enjoy!

Matt Gallagher deserves a medal…

Every once in awhile you read a blog post that completely changes the way you think about a problem. Matt Gallagher’s Cocoa With Love is one of those blogs where it happens often. If you’re not subscribing to his RSS feed, do it now.

In particular, this post addressed a problem that every iPhone developer has encountered: any kind of settings or form UI is a pain in the butt if you follow Apple’s sample code. And, of course, this is exacerbated because these essential interfaces aren’t very fun to code. For both Twitterrific and Frenzic, it’s been a frustrating experience: I wanted to fix how this standard UI was created and maintained.

The motivation

Before I start talking about what I did using Matt’s concepts and code, I’d like to discuss the need for settings in an application.

There are some people who think that settings should only be in the Settings app. There are others that think they should only be within the application. I can honestly see how both groups are right, but what both sides fail to realize is that it’s often a matter of context.

If you’re working on a simple application with simple needs, relying on the infrastructure provided by the SDK is fine. You may have some additional support load for users who have problems finding your settings, but that’s a reasonable tradeoff for saving development time. Sophia Teutschler’s Groceries app is a fine example of where the built-in settings shine: I turned off the Marker Felt font several months ago, and I haven’t touched it since.

In the case of Twitterrific, there were three main reasons why we built the settings into the application:

  1. We could not split the settings between two locations. From a user’s point-of-view, it’s incredibly confusing to have an application’s configuration in two places. This would have happened if we had put the account settings in the application and everything else in the Settings app.
  2. Some of the settings are things that people will change on a fairly frequent basis. For example, the dark theme works best at night, while a light theme is better during the day. Leaving the app to make these types of adjustments is inconvenient.
  3. The Settings app can’t handle preferences that are “dynamic.” An example is a vibration setting for the notification: there’s no way to make this appear on an iPhone but not on an iPod touch.
As we start to see more complex applications appearing on the App Store, I think there will be a lot of other developers coming to grips with settings in their applications. That’s where my code comes in…

The solution

I’ve taken the basic concepts that Matt presented in his article and extended them to create a pixel perfect replica of what you see in the Settings application. Instead of configuring applications using a property list, you do it with some very simple code in a view controller. (Domain-specific languages, such as those found in Ruby, were an inspiration for the methods used in this code.)

And if you’re not doing settings, the classes are still very handy for making user input forms. They were used in all of the search forms used in the new version of Twitterrific.

To give you a quick taste of how it’s used, here’s a complete implementation of a table view controller that presents a single group with a text field and a switch control. In just six lines of code:

- (void)constructTableGroups
{
  NSMutableArray *cells = [NSMutableArray array];
  IFTextCellController *textCell = [[[IFTextCellController alloc] initWithLabel:@"Text" andPlaceholder:@"Placeholder" atKey:@"sampleText" inModel:model] autorelease];
  [cells addObject:textCell];
  IFSwitchCellController *switchCell = [[[IFSwitchCellController alloc] initWithLabel:@"Switch" atKey:@"sampleSwitch" inModel:model] autorelease];
  [cells addObject:switchCell];
  tableGroups = [[NSArray arrayWithObject:cells] retain];
}

The real beauty of this code is that it’s incredibly easy to change. Settings and forms tend to evolve over the lifetime of a project, so using these simple declarations for the table view layouts makes it much simpler to adapt to new requirements. If I need to change the switch control shown above into a choice list, I can do it in a couple of lines of code.

So without further ado, here’s the source code in a sample project. The code is well documented, so you shouldn’t have any problems figuring out how it all works. Start by looking at the RootViewController, then take a look at the SampleViewController, followed by the SampleAdvancedViewController. If you’d like to incorporate this code into your own project, just grab all the classes with the Iconfactory prefix (“IF”.)

The license

Previously, I’ve released source code on this site without any licensing at all. This time, I’m going to try something new. You can do anything you want with this code with one requirement: you need to give the Iconfactory some link love in your product.

For more details on the licensing terms, check out the Licensing.rtf file that’s included in the sample project. If you can’t abide by this restriction, please get in contact and we can try to work something out.

I hope this code saves you as much time as it has saved me. Enjoy!

A thought experiment

Assume the following:

  • You have an application that you’re selling on the App Store. This application, MyApp 1.0, works on both iPhones and iPod touches with the 2.2.1 firmware.
  • The compelling new APIs in iPhone SDK 3.0 allow you to implement a bunch of great new features in your product. Let’s say you add a Map View and release MyApp 2.0 (after SDK 3.0 is released.)

Now, what happens if you find a bug in MyApp? Let’s say it’s a simple thing like dereferencing a nil pointer that causes a crash: something that can be fixed with a single line of code. You easily fix this bug, but you can’t give this fix to all of your customers. Why?

The problem lies with iTunes Connect. It only allows you to upload a single binary. And that single binary is specified to work with a single version of the iPhone firmware. Even if you have branching tools in your version control system, you can’t use them to produce an update for both versions of the SDK.

This presents a problem for customers who are still running the 2.2.1 firmware: they can’t get your fix until they upgrade to the 3.0 firmware.

Despite the fact that there will be a lot of uptake on this new release and all its great new features, it still feels wrong that an iPod touch owner will need buy the update in order to get my fix. I don’t like it when customers have to pay for my stupidity.

If you agree, please dupe Radar ID 6735814. Thanks!

Slow ride, make it easy

Many of us are developing iPhone applications running in a simulator connected to a very fast Internet connection. Too bad the customers of these applications won’t be using the same environment.

It’s very important to be able to profile and debug your application while it’s running on a slow network. You’ll find lots of weird timing problems, bad connection error handling, timeouts that are too short, and other things that are likely to occur in real world conditions.

Initially, I did testing for Twitterrific by loading the latest version onto the device and then walking around outside away from my office Wi-Fi network. That was a good first step, but also one that didn’t allow me to run gdb or Instruments when a problem occurred.

And then one day it hit me: the iPhone Simulator is running on top of the Mac OS X network stack. An environment that I can manage with standard Unix command line tools. Here’s the result:

#!/bin/bash

# configuration
host1="twitter.com"
host2="search.twitter.com"

# usage
if [ "$*" == "" ]; then
  echo "usage: $0 [full|fast|medium|slow|wwdc|off]"
  exit
fi

# remove any previous firewall rules
sudo ipfw list 100 > /dev/null 2>&1
if [ $? -eq 0 ]; then
  sudo ipfw delete 100 > /dev/null 2>&1
fi
sudo ipfw list 110 > /dev/null 2>&1
if [ $? -eq 0 ]; then
  sudo ipfw delete 110 > /dev/null 2>&1
fi
sudo ipfw list 200 > /dev/null 2>&1
if [ $? -eq 0 ]; then
  sudo ipfw delete 200 > /dev/null 2>&1
fi
sudo ipfw list 210 > /dev/null 2>&1
if [ $? -eq 0 ]; then
  sudo ipfw delete 210 > /dev/null 2>&1
fi

# process the command line option
if [ "$1" == "full" ]; then
  echo "full speed"
elif [ "$1" == "off" ]; then
  # add rules to deny any connections to configured host
  if [ -n "$host1" ]; then
    sudo ipfw add 100 deny tcp from $host1 to me
    sudo ipfw add 110 deny tcp from me to $host1
  fi
  if [ -n "$host2" ]; then
    sudo ipfw add 200 deny tcp from $host2 to me
    sudo ipfw add 210 deny tcp from me to $host2
  fi
else
  # create a pipe with limited bandwidth
  bandwidth="100Kbit"
  if [ "$1" == "fast" ]; then
    bandwidth="300Kbit"
  elif [ "$1" == "slow" ]; then
    bandwidth="10Kbit"
  elif [ "$1" == "wwdc" ]; then
    bandwidth="1Kbit"
  fi
  sudo ipfw pipe 1 config bw $bandwidth

  # add rules to use bandwidth limited pipe
  if [ -n "$host1" ]; then
    sudo ipfw add 100 pipe 1 tcp from $host1 to me
    sudo ipfw add 110 pipe 1 tcp from me to $host1
  fi
  if [ -n "$host2" ]; then
    sudo ipfw add 200 pipe 1 tcp from $host2 to me
    sudo ipfw add 210 pipe 1 tcp from me to $host2
  fi
fi

sudo ipfw list

You’ll notice a couple of configuration parameters: host1 and host2. Unless you’re one of the hundreds of Twitter client developers, you’ll probably want to change those values.

Turning a connection “off” can be used to simulate a site being offline. Using the “wwdc” setting allows you to relive those exciting moments of waiting in line with thousands of other geeks banging on the SOMA EDGE network. To turn off the bandwidth limit, use the “full” setting. The other values can be used to alter the quality of the connection in the simulator and make it feel more like it’s on a cellular network.

(I actually uncovered a bug in the beta version of Twitterrific while waiting in line for the WWDC 2008 keynote, so don’t think that setting is purely a joke.)

This bandwidth trick can be extended for device testing. On your development machine, turn on Internet Sharing (in System Preferences > Sharing.) Then use your device to connect to that shared network. Any ipfw rules you set will affect the device since all of its packets pass through the gateway you’ve established on your machine.

Another trick you can use to test your networking code is by opening the Networking preference panel and making your outbound connection inactive. Since the iPhone Simulator’s networking code sits on top of the System Configuration framework, any state changes will be passed onto your app running in the simulator. (If you’re using the Reachability class, this will make sense.)

And as I mentioned above, being able to do all these things from the comfort of your office chair and Xcode debugging environment has a lot of advantages. So slow down and do some real testing!

Updated March 25th, 2009: Mike Schrag has written a nice System Preference panel called Speed Limit which does the same thing in a nice GUI. You really have no excuse now :-)