Wednesday, October 15, 2008

iPhone app version and build numbers

I think every iPhone developer should know how to assign version and build numbers to their app. If you have come from another platform, like I have, the Apple way is the same but different. I am standing on the shoulders of Chris Hansen for a lot of this post. His post on Xcode: Build numbers and versioning with agvtool was the starting place for me on a lot of this information. Chris' post is nearly 3 years old now, Apple has moved some stuff around and it isn't iPhone specific. Plus, there are no screenshots :-)

Marketing or Build Version?
Apple's iPhone application templates create an Info.plist in the Resources group for you that is configured like this:

Highlighted is the Bundle version with a default value of 1.0. This was exactly how iTimeZone 1.0 was configured. For iTimeZone 1.1, I wanted to be able to show both the marketing version (Version 1.1) and the build number (Build 14), and toggle between them in the app, since I was going to distribute the app to beta testers. So i thought Bundle version was the marketing version because the templates fill it out with 1.0, but that is not the whole truth. Since the iPhone app templates don't really do anything with Bundle version, you have to look at a Cocoa OS X sample application to see the right behavior.

When you only have Bundle version, in a Cocoa OS X app you see a built-in About box that looks like this:

When you add a new key to Info.plist called Bundle version string, short, and flip your 1.0 into that key, and then put something more traditionally resembling a build number (14) back into Bundle version, the Cocoa OS X built-in About box looks like this:

So which keys you should use on the iPhone are pretty clear, Bundle version is really your build number key, and Bundle version string, short is really your marketing version. Now lets build something.

Build It
Whenever you hit Command-B in Xcode, you are creating a build. As a developer you are building all day long. But a developer build and a build you intend for distribution are two different things. When a build is distributed, you need to assign it a unique identifier so you can ask people what version they are on or for bug reporting purposes at a minimum. This is usually referred to as the build number. I am not talking about continuous integration here, only the manual process, but if you wanted to setup continuous integration, you just need to automate what I talking about.

Apple has a tool, called agvtool, to increment build numbers, the Bundle version in your Info.plist, as well as make that number a variable in your code. The tool is called agvtool for Apple-generic versioning tool for Xcode projects. In order for it to be usable, you have to change a few build settings in the Versioning section for All Configurations. Here is the screenshot of iTimeZone 1.1:

The key setting is Versioning System. This must be set to apple-generic. I don't know of any other possible setting except None. This does not mean that Xcode now automatically increments a build number for you, you have to take specific action to get the build number incremented. Now that those are set, you have to close your project, open Terminal, and do the following to increment your build number:
/Developer/usr/bin/agvtool next-version -all

This was one of differences with Chris Hansen's post, Apple moved the location of the tool. Also, I encourage you to hit man agvtool for all the options, like automatic check-ins to source control on running agvtool, but I haven't set those up yet. Now you have pretty much everything you need to use the build number system, but you need to be able to use this information in your iPhone app.

Using Build and Version Numbers In Your iPhone App
Unfortunately, there really isn't at the time of this writing a defined pattern for displaying version numbers or build numbers in iPhone applications. In iTimeZone 1.1, I choose to put a dedicated About screen into the app. Not only to show version and build numbers, but a couple of links to give credit for usage of assets in the application, and double-tappable icons to the iTimeZone and Tangerine Element web sites.

When you tap on any whitespace, or directly on the version number, you see the build number. To show these properties in your app, you need to know what the real names of the keys are. If you right-click on any of the properties in the Info.plist view, click Show Raw Keys/Values to see a view like this:

I created an AboutViewController with a corresponding XIB. In the viewDidLoad method, I set the UILabel to the version number by doing this:

[NSString stringWithFormat:@"Version %@",[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]];

When the user taps the view, a BOOL is used to track what state the view is in. When the build number should be changed, the UILabel is set to the the other number, so for the build number:

[NSString stringWithFormat:@"Build %@",[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]];

Wrap Up
So I think that's it. Let me first say, I am not saying you have to do an About screen in your app, I didn't have one in iTimeZone 1.0. That said, unless I am just not thinking outside the box enough on this, I can't see how to conduct a beta test without being able to check the build number, and there is simply no facility for doing that built-in to iTunes or the iPhone. You can probably hide this in your app elsewhere, but it probably needs to be there.