Setting iPhone Application Build Versions

I have mentioned in the past that I make use of agvtool to set the build number of my Xcode projects but I have never, until now, documented the process.

Marketing Release Version

The first thing to understand is the Apple generic versioning system and in particular the difference between the marketing version of an application and its internal development bundle version. The marketing version is the release version number of the application as presented to the end user. The Apple recommendation is to use three dot-separated integers as follows:

X.Y.Z
X - major revision number
Y - minor revision number
Z - maintenance or patch release

Since this is a marketing controlled version number you can decide to release version 1.5.1 of an application and then follow it with a version which includes a major upgrade numbered 2.0.0

Build Version

The build version number identifies a particular internal build of an application bundle. The internal build number may or may not correspond to an external release. Typically you may have several internal releases before issuing an external release that will also have a marketing version. The format of the build version string is largely up to you though you should still make it one or more dot-separated integers. I tend to just use a single integer (1,2,3,4…) but you can also use 1.0,1.1,1.2, etc. if you have need for it.

Information Property List File (Info.plist)

Both the marketing release version number and the build version number are stored in an application targets Info.plist file. The two keys of interest are as follows:

  • CFBundleVersion - build version number
  • CFBundleShortVersionString - release (marketing) version number

It is important to note that the internal build number is really just the Xcode bundle version string. This means that an Xcode project with multiple targets will have the same build version number for all targets. Each target can still have a unique marketing version but must share a bundle version. So for example, bundle version 53 may contain two targets one with a marketing version of 1.2.4 and the other with 1.0.8.

To setup a new project to follow the Apple generic versioning system you simply need to make sure each target in the project contains an Info.plist file with the two version keys initialised to your preferred values (e.g. you might start a new project with bundle version 1 and marketing version 1.0.0)

Incrementing Build Versions

I have seen several different strategies for incrementing the build version. Some people increment the build version each time they commit code to a code repository - in fact most version control systems have sufficient hooks to allow you to automate this. Since I am using Git I tend to only increment the bundle version when I reach a major project milestone. That milestone may correspond to an external release but not always. Whatever you decide I guess the important thing is to be consistent.

[As an aside I also store the first few characters of the Git commit name in a separate Info.plist key. See this earlier post for details.]

Automating with the Apple Generic Versioning Tool

You could manage the build numbers in your Info.plist files manually but, especially if you have multiple targets, it is much easier to do it using the agvtool that Apple includes with Xcode. On my system running 10.6.4 (Snow Leopard) with Xcode 3.2.3 the agvtool executable is at /Developer/usr/bin/agvtool. There is also a wrapper script at /usr/bin/agvtool that will call the correct version of agvtool if you have multiple Xcode installation folders. For the purposes of this discussion it makes no difference which way you invoke agvtool.

Updating the Bundle Version Number

To update the bundle version number (CFBundleVersion) for all targets in a project:

$ agvtool next-version -all

This will increment the value of the CFBundleVersion key in each Info.plist file contained in the project. So bundle version 10 will become bundle version 11. If you are using a more complex version number it will be incremented to the next major version number, so 2.5 will become 3 (another reason I do not bother with more complex build numbers). If you want to force the bundle version to a specific value you can do that as follows:

$ agvtool new-version -all 2.6

Updating the Marketing Release Version Number

You can also manage the marketing release version number with agvtool but this will not do what you want if you have multiple targets in a project each requiring a different marketing version number. The problem is that agvtool processes all Info.plist files and sets the key to the same value. This is fine for the bundle version which is the same for all targets but not for the marketing version.

Therefore each time you issue an external release you should set the CFBundleShortVersionString to your marketing (external release) version manually in the Info.plist for the target you are releasing.

Viewing Version Numbers

To view the bundle version number of the marking version numbers (one for each target):

$ agvtool what-version
$ agvtool what-marketing-version

Displaying Version Numbers

Since the version numbers are all stored in the Info.plist file for the current target it is easy to access the values to display them within an application:

NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *name = [infoDictionary objectForKey:@"CFBundleDisplayName"];
NSString *version = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
NSString *build = [infoDictionary objectForKey:@"CFBundleVersion"];
NSString *label = [NSString stringWithFormat:@"%@ v%@ (build %@)",
                   name,version,build];

This will create a string containing the application name followed by the release version number with the internal build number in brackets. For example: “MyApp v2.0.1 (29)”.