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 :-)