The final test

During the early days of iPhone software development, there were no mechanisms for doing beta tests. Those of us on the bleeding edge were developing apps with very little peer review and beta testing.

Luckily, I have friends who are a lot smarter than me. And we banded together with one primary rule: you don’t talk about Fight Club. Despite the NDA, we were determined to test each others applications.

This was accomplished by distributing unsigned applications amongst the group. Each member of the group then signed the app with their own developer certificate and installed it with Xcode. We all got pretty good with the codesign utility because it was so exciting to see each others work.

But this essay isn’t about reminiscing. It’s about telling you how to test the applications you upload to iTunes Connect.

If you’ve submitted an application to the App Store, you know that sinking feeling of not being able to test your final build. The binary that gets signed with the “App Store” distribution mechanism cannot be run on your test devices: you can’t be sure that the final bits you send to Apple are complete.

But there’s a simple way around this problem and it’s called codesign. The same application we used for our pre-Ad Hoc beta testing can also be used for a final test on your App Store submissions.

The first step is to create your distribution build for the App Store. For this example, we’ll be using the Frenzic application and putting it into build/Distribution-iphoneos. After the build completes, make the ZIP file archive immediately. Put it in a safe place so you can upload it to iTunes Connect after you finish your final test.

Now verify that you’re dealing with the right application. Go to the build folder and issue the following command:

$ cd build/Distribution-iphoneos
$ codesign -d -vv Frenzic.app/Frenzic

You’ll then see a verbose dump of the signature in the application:

Executable=/Users/craig/Projects/FrenzicTouch/build/Distribution-iphoneos/Frenzic.app/Frenzic
Identifier=com.iconfactory.Frenzic
Format=bundle with Mach-O thin (armv6)
...
Signature size=4331
Authority=iPhone Distribution: The Iconfactory
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Nov 12, 2008 1:46:07 PM
...

The most important part of that output are the lines beginning with “Authority”. It shows that the “iPhone Distribution” certificate for The Iconfactory is being used. This is what Apple needs, but it’s also the thing that keeping you from running the app on your test devices.

So let’s change it.

The first thing you need to do is define an environment variable that gets the correct version of codesign_allocate (the one in /usr/bin doesn’t currently support ARM):

$ export CODESIGN_ALLOCATE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate

If you forget to do this, you’ll see this error message when you try to sign the binary:

codesign_allocate: unknown architecture specification flag: -a armv6 6720

Now you can sign the application using your own certificate:

$ codesign -f -s "iPhone Developer" -vv Frenzic.app/Frenzic

The -s parameter should be the same value as the Code Signing Identity > Any iPhone OS Device setting in your Xcode project. The -f parameter tells codesign to force the replacement of the signature. As the command executes, you’ll see the following output as the signature is replaced:

Frenzic.app/Frenzic: replacing existing signature
Frenzic.app/Frenzic: signed bundle with Mach-O thin (armv6) [com.iconfactory.Frenzic]

After you’ve re-signed the app, you’ll see that I am now the authority. But you probably already knew that:

$ codesign -d -vv Frenzic.app/Frenzic Executable=/Users/craig/Projects/FrenzicTouch/build/Distribution-iphoneos/Frenzic.app/Frenzic
Identifier=com.iconfactory.Frenzic
Format=bundle with Mach-O thin (armv6)
...
Signature size=4302
Authority=iPhone Developer: Craig Hockenberry
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Nov 12, 2008 6:35:06 PM
...

Now you can move the updated application onto a test device and make sure that the final build is OK. Everything except the signature is the same: if there’s a missing file or something that’s messed up, you’ll be able to find it before the QA engineers at Apple do (and with 10 day turnaround times, that’s a very good thing.)

Once you’re happy that everything looks good, upload the ZIP file you created earlier to iTunes Connect and crack open a beer. As you’re enjoying that beer, take a look at the codesign man page for more information on this essential utility. Or write an essay for your web log :-)

Updated July 2nd, 2009: Note that this procedure can also be used if you are beta testing. This is particularly helpful in cases where you’ve upgraded to an iPhone 3GS and the Ad Hoc .mobileprovision file for the beta contains the UDID for your older device. Many developers have hit the 100 device limit since the release of the new phone, and can’t add devices into a new .mobileprovision file. You’ll be able to test the beta releases after signing the code with your own certificate. The only case where this won’t work is when the app being resigned uses the keychain.

Debugging with backups

If you’ve written an application for the iPhone, you’ll eventually encounter a customer problem that you can’t reproduce. Of course, you’ll want to get a copy of the customer’s data and preferences so you can replicate the environment they are working in. And then you’ll realize that it’s a total pain in the ass for both you and your customer.

iTunes creates backups every time a user syncs their device, but these backups are meant to be read by a machine, not a human. Your customer will have to wade through a bunch of GUIDs in the MobileSync/Backup folder: getting the right backup for your application has been a headache.

Fortunately, things got much better last week with the release of Pádraig Kennedy’s iPhone Backup Extractor. Pádraig is the author of the Python script some of us have been using to extract data from the .mdbackup files. Having this script in a GUI that the end user can run is a huge improvement.

You can instruct your customer to download the application, sync their device with iTunes and then have them select the latest backup and your application within that backup. The resulting output contains the documents and preferences in use by the application at the time it was synced and can easily be put in a ZIP archive.

Getting this information into your development environment is then just a matter of hacking around with the Simulator folder structure in ~/Library/Application Support/iPhone Simulator/User/Applications. The bug won’t stand a chance at this point :-)

Thankfully, Pádraig has made this a free application. Your customers won’t need to pay for anything before they help you out. You, on the other hand, won’t be using it directly but will still benefit immensely. Make sure to click on the Donate button so that this application will continue to see development and maintenance. I just did.

Splash screens

Twitterrific has a splash screen and I would like to get rid of it. But I can’t.

Splash screens hurt the user experience from a purely psychological point-of-view. They don’t change the launch time of your iPhone application at all, but it looks and feels longer.

But there’s a problem: you can only specify one Default.png file to be displayed at launch time. Unfortunately, applications can have many visual states which you’d like to show as the code is loaded. In the case of Twitterrific, the list or detail view can be active and they have no visual commonality.

So what are the current options?

Some people have suggested that you have a single startup image (like the list view in Twitterrific) and use a Core Animation transition to the actual state the user was last in. This would work, of course, but it has a major flaw: it increases the amount of time needed before the user can actually start using the application (they need to wait for the transition to finish.)

Another option would be to show a blank screen. I tried this, and my first thought was that the application had crashed. Not acceptable.

Why not replace the Default.png on-the-fly? As Tom Insam notes, you can’t modify the application bundle: doing so breaks the application signing and will leave you with code that won’t run.

So that leaves us with splash screens. The user knows the launch is in progress, there are no jarring visual changes, and it’s the quickest way to get to an active state.

Of course, there are mechanisms to have multiple startup screens. You can see it in the Clocks app: the world clocks view has a different startup image than the stopwatch view. Another example are the new chrome-less Safari pages that you can put on the home screen: these apps take a snapshot of the current screen at exit and display it at the next launch.

Apple should expose this functionality to third parties. If you agree, please submit a duplicate for Radar ID# 5872097. Until that happens, please excuse our splash screen.

Fancy UILabels

People have asked which part of our Twitterrific application for the iPhone was hardest to develop. There were many challenges, but the one I found most onerous was scrolling in the UITableView.

The code we shipped in 1.0 was obviously flawed. Scrolling was jerky. We weren’t happy with it and neither were users.

There was no shortage of “expert” opinions on what was causing it. Some thought it was the loading of the avatar images, others thought it was the fancy backgrounds. One reviewer on iTunes even suggested that I could spend a couple of hours reading documentation on Apple’s site and fix it up.

Yeah, right.

The root of the problem was having links inline with the text: there are currently no attributed strings in the iPhone SDK. Before displaying a table row, I had to scan the text of the tweet for URLs and screen names, measure the length of text, and then create a bunch of views that approximated what people were used to seeing as HTML. This overwhelmed the CPU on the device: the GPU was having no problem compositing the layers. I had heeded the advice of experts and was careful to flatten the hierarchy as much as possible and to use opacity sparingly: only NSString’s sizeWithFont: was killing me.

So what changed in the 1.1 release? A slight interaction change and a new approach to rendering the UIButtons used for the links in the text.

The interaction change was that no text was styled when a table row is unselected. This allowed us to use a plain UILabel for the text of the tweet. Since only one tweet is selected at any time, this simplified the text layout requirements when reusing a table row.

Since UILabel was being used for rendering the text, our approach for measuring text changed as well. Instead of controlling all the text layout, I had to adapt my code to use metrics and line breaking that matched those used in UILabel’s implementation. They call it reverse engineering, kids.

This approach is far from perfect. There are cases where the buttons are placed incorrectly: I suspect that the cause of these problems is how round-off error is being handled in the method used by UILabel’s underlying WebView and the method used by sizeWithFont:. A half-pixel being rounded up versus down can cause a line break or the button text to not align with the underlying label. These errors seem to occur more frequently with Asian fonts.

There is also an issue with a URL that wraps a line: the button is only created for the first part of the URL (leading some users to think that the button won’t work—it will.)

In essence, it’s a hack. And useful one until Apple deals with Radar ID# 5834539 (titled “There is no way to style a run of text in a UILabel or UITextView”.) It wouldn’t hurt to dupe it if you’d like to see this aspect of the iPhone SDK improve.

Now that the FABULOUS NDA is a thing of the past, there’s no reason why you should have to figure this stuff out like I did. Here’s my implementation. Knock yourselves out customizing this code for your own needs. All I ask is that if you make any improvements in createButtons:, please let me know (my contact info is on my résumé page.)

But wait, there’s more!

And an extra added bonus, this project also shows you how to use regular expressions using RegexKitLite and libicucore. Extra pattern matching goodness, for all your iPhone needs.

Damn it feels good to leave out that NDA disclaimer at the bottom of this essay.

Updated December 4th, 2008: In the original essay, I forgot to link to the RegexKitLite page at SourceForge. This code by John Engelhart is so damn useful, I tend to think about it as a standard part of OS X. Take a moment and check out this project’s exemplary documentation.

[REDACTED]

Thank God—that’s the last time I’m going to type that word for awhile. The meme is dead, long live the SDK.

As a way to celebrate the lifting of the NDA, we bring you some very special source code. To wile away the time between our product submission and the launch of the App Store, my buddy Anthony Piraino and I worked on this very special treat. Something that will be familiar to all developers who have had to keep their mouths shut since March 6th, 2008. Just compile the source code and install it on your device. Typing pleasures await.

(You’ll need to install Twitterrific from the App Store to get the full user experience. But you’ve done that already, right?)

Besides being a fun inside joke, this very special application also shows an important aspect of iPhone development: URL schemes.

As we’ve seen many other times, the needs of a mobile user are very different than those of a desktop user. On the desktop, tight integration of several application domains makes applications like Coda a joy to use.

On the phone it’s better to focus on one task. From what I’ve seen, the best iPhone applications do one thing and do it well. Supporting URL schemes in your application makes that single task more attractive to other developers and users. It leads to what my friend Daniel Jalkut has aptly called the “Un-Coda-fication” of iPhone apps.

The benefit for a developer is obvious: it minimizes the scope of an application and the attendant memory footprint. You could write your own Twitter update code using a NSURLConnection, or you could use one line of code like this:

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"twitterrific:///post?message=EASY"]];

There is a less obvious, but equally important benefit when you look at URL schemes from a user’s point-of-view. Since your application’s scope is limited to one task, users will depend on it when they want to perform that task. Even if that task is in the context of another application.

An example of this is sharing photos. Users know that the Camera application takes pictures and that the Mail application sends messages. You don’t see a camera button in Mail; you see an “Email Photo” button in your Camera Roll. The user’s first task it to take a picture and the next task is to mail it.

Since we’re not Apple, we can’t achieve the high level of integration between the Camera Roll and the Mail application. But we can use URL schemes to accomplish much the same thing.

I worked with Fraser Speirs and Ian Baird during the development of Exposure and Cocktails so that their applications could support a “Post to Twitter” button. Clicking on that button initiates a workflow that lets the user share what they’re looking at on Flickr or what kind of drink they’re enjoying. Leaving their app/task and launching Twitterrific makes complete sense.

If you’d like to include a “Post to Twitter” button in your own application, all the code you need is in the postToTwitter: method in the very special application mentioned above. If you want to handle your own URL scheme, take a look at application:handleOpenURL: in the application delegate. Be careful about validating inputs: you don’t want malicious URLs to do bad things.

Not to dampen your enthusiasm, but please be aware of a couple of limitations with URL schemes. First, there is no way to know if a URL scheme is supported or not (rdar://problem/5726829). Currently, the best you can do is to performSelector:withObject:afterDelay: then openURL:. If the selector gets called you know that the URL failed to open. Also, be aware that deleting an application can sometimes leave the URL registration database in a state where it no longer recognizes a scheme (rdar://problem/6045562). This only happens when two applications support the same URL scheme, so you can avoid the problem by using a unique scheme name. Please use the Radar bug ID# for a “me too” bug report if this becomes a problem in your own application.

Now let’s enjoy our newfound freedom to discuss the iPhone SDK and the first of many sample code releases on this site!

Update October 1st, 2008: As Jonathan Rentzsch and Thomas Ptacek point out, URL schemes can be an attack vector for your application. Pay particular attention to the code in -application:handleOpenURL: in your application delegate. If you find any vulnerabilities in my code, please let me know so I can update this essay. Thanks!

Update October 7th, 2008: Once your application supports URL schemes, it’s likely that you’ll want to provide a bookmarklet in Safari. Here’s the one we use in Twitterrific:

javascript:window.location='twitterrific:///post?message='+escape(window.location)

The hardest part about doing this is guiding the user through the setup process. Joe Maller came up with a simple solution that lets the user get the Javascript into their bookmark list. This was later refined by Alexander Griekspoor. Make sure to view the source on these pages for additional hints and installation instructions.

If you agree that this setup process is too difficult for end users, please submit a duplicate bug for Radar ID# 5935641. Thanks!

Update February 4th, 2012: UIApplication now provides a -canOpenURL: method that lets you check if there is an application installed that supports the URL scheme.