How to test your app on older iOS releases

In this week’s Build and Analyze, Marco Arment talks about supporting older iOS versions in Instapaper. As he says, being able to test your app on those versions is not easy, and just getting a device to run a specific iOS release can need “hacky jailbreak tools” or can even be impossible. Since I spend my time with those (admittedly hacky and confusing) tools and have an understanding of how the restrictions here work, I thought I’d write an explanation and a guide on how to successfully test for older iOS releases.

Background

Originally, there weren’t any issues with running any iOS release on your device. You just download the firmware (ipsw) file, open up iTunes, and restore with it. This method worked through iPhone OS 2.0, the iPhone 3G, and the second generation iPod touch.

However, with the iPhone 3GS, Apple changed everything. On that device (as well as the iPod touch (third generation), the original iPad, and the iPhone 4), each and every firmware change must be approved by Apple’s servers, at the time of the install. And Apple will only agree to let you install the current latest release of iOS at that time, which prevents downgrading — as well as any re-installs of the iOS release the device is running, as long as that release is not the absolute latest version available.

The iPad 2, iPhone 4S, and iPad (third generation) all use similar methods, with additional security measures. (I’ll talk about those later.) In addition, with iOS 4, similar protections were added to the iPhone 3G and iPod touch (second generation). While those devices can still be easily restored to pre-iOS 4 releases, it’s no longer easily possible to restore them to any version from iOS 4.0 on.

Why It’s Important

What Apple has done here, essentially, is sacrifice the ability for developers to test code on older versions of iOS, for the sake of increased control of how we use our devices. It’s hard to see a benefit: if someone is knowledgeable to downgrade a device, they probably also have a good reason to.

It’s unclear that Apple realizes what this does to developers. Xcode, even, doesn’t realize that this happens at all: the Organizer still has a file picker and version selector under “Restore”, oblivious to the fact that this option no longer, in fact, works (and hasn’t for almost three years). The enterprise iPhone Configuration Utility and education Apple Configuration tool are similarly confused.

A good, simple solution would be for Apple to simply allow devices registered for development to install older iOS releases, just as they are allowed to install betas. (To encourage Apple to fix this problem for developers, please dupe my radar and help call attention to it. Thanks!)

What To Do

Often, even if Apple doesn’t support it, you still do need to ensure your code runs on older iOS releases. Since you can’t trivially restore your device to an older version, I’ve made a list of the possible workarounds:

  • Use the Simulator

    This is definitely the easiest method. Rather than testing your app on a device, you just use the iOS Simulator to test it on that version. While Xcode itself only is bundled with the simulators for the latest two iOS releases, it’s pretty easy to import older ones: just drag the SDK bundle (iPhoneSimulatorX.Y.sdk) into the iPhoneSimulator.platform/Developer/SDKs folder. If you didn’t keep around copies of the older SDK, they are very findable online. Update: for registered iOS developers, you can apparently find old versions of Xcode you can extract the SDKs from available on Apple’s developer download page.

    Update: It’s even worse now, Apple appears to be blocking the iOS 4.3 simulator on Mountain Lion. I haven’t tested it, but you might be able to install it manually.

  • Save Devices

    The easier method, although the most expensive, is to simply begin a collection of devices running various iOS releases. Most developers I’ve talked to take this route, but it’s hard to enter. First, it’s expensive to buy all the iOS devices necessary to test each release. Secondly, it’s quite difficult to obtain a complete spread of iOS releases to test on. Since downgrading is blocked, you have to purchase these devices still running older releases — something that gets harder every day, as more older devices break and get replaced.

    And, once you have the devices, you have to be very careful to never break them — or to accidentally accept the endless stream of update prompts from iTunes. Remember: even if you just need to restore the device to the same older version of iOS as is already on the device, Apple doesn’t even allow that. So be very sure that they never get messed up.

  • Evading SHSH (the “hacky jailbreak tools” method)

    When Apple accepts your request to restore a device, they give back a cryptographic agreement to allow it, called an “SHSH blob”. In the iPhone 3GS, iPhone 4, original iPad, and iPod touch (third generation), however, Apple forgot to include one crucial piece of data in these signed blobs: a time range of when that acceptance was valid for. Using that mistake, Jay Freeman (saurik) developed a massive man-in-the-middle attack: storing the authorized responses for millions of device/firmware combinations, and replaying them to restore the device on demand.

    If did happen to store your “SHSH blobs” for a specific iOS version with the Cydia server for one of these devices when Apple was still signing that release (most jailbroken users probably did), you need to do some tricks to use the saved response.

    The tools for this are TinyUmbrella and redsn0w. First, use TinyUmbrella to download the SHSH files saved for your device. (Be sure to have “Request from Cydia” checked so it asks Cydia’s server, rather than Apple, for the saved blobs.) Then, use redsn0w to create an IPSW you can restore in iTunes.

    To save SHSH blobs for the future, also use TinyUmbrella, just have it ask Apple (uncheck “Request from Cydia”). If you didn’t manage to save this data but do have one of these devices on an older iOS release but want to extract the data for the version it is running, it is actually possible to do so. Use redsn0w and choose “Fetch” to copy the blobs off the device.

    Unfortunately, this loophole was essentially fixed in the iPad 2, iPad (third generation), and the iPhone 4S. (It may still be possible with GSM and Wi-Fi only models of the iPad 2, but don’t rely on it.)

Apple, please help!

The only way for this issue to be truly resolved for developers is for Apple to create a supported method to downgrade devices to test apps. While the iOS 5 over-the-air updates do appear to increase update penetration and speed, it’s simply not possible for many apps to only be available (or, worse: only tested) on the very latest version of iOS.

Right now, it does not seem likely that Apple will change anything here, so we are stuck with the workarounds above. However, there’s a chance that they will, and more people letting them know it’s an issue can only help.

Let me know if you know of any other workarounds or things I missed: I’m on Twitter as @chpwn, or you can email me at chpwn@chpwn.com.