Recipe: Automatic Version Number, Build Number & Build Date Handling
Dealing with build and version numbers in applications whether for Desktop MacOSX or iPhone applications always seems like a bit of a black art, here’s the short version of how to set up a consistent and maintainable system for dealing with version numbers in Xcode.
The Basics:
- There are two distinct kinds of version numbers in iPhone and MacOSX apps:
- User readable version numbers – these are what you typically see in about boxes. This is often called the “marketing version number,” like “OS X version 10.6.6.” It’s a convenient string for public consumption.
- The Build Number – this is the “real” version number that represents the a milestone in your distribution of the product you are releasing – for example, you may be planning a marketing release of MyGreatApp version 1.5, but before you get there you will have a number of builds that get sent out to your beta testers, as you fix bugs or add feature your take a snapshot at a given point and release a new build for testing. This may seem counter intuitive but think about large software product like Mac OSX: Apple may release many builds of OSX before the release a “final” build that represents a given “marketing version” of the shipping product (which itself cold contain many, many components each with their own build numbers). If you have ever gotten MacOSX developer seeds they may all have the same “version number” (e.g., 10.6.3) but vary in the build number.
- Addition many developers insert a Build Date – this is literally the date/time stamp of when a binary was compiled; this could be useful when sending crash reports or even as part of a “contact us” form that the user uses to drop you a support note where the program version info is included
- All of these elements are stored as entries a Plist file attached to an Xcode target.
- Almost anything that you can create at a target level that exists as bundle style file can have a distinct version number that you can manage. This includes iPhone static libraries, OSX frameworks, Bundles, Plugins, and of course iOS and desktop apps. Stand-alone BSD libs and C++ style libraries cannot use this mechanism, but instead can use more traditional embedded versioning appropriate to these library formats.
- Version numbers in PLists are updated by agvtool (the Apple Generic Versioning Tool) that can increment all the standard parts of a traditional components version number (major, minor, release type)
- Build numbers and Build Dates are updated by script “build phases” that you add to your target
Setting it up:
When you create a new project or a new target in a project, in addition to all the supporting source code files, and potentially NIB/XIB files there will be an Info.plist file whose prefix is the name of the actual target. So if you’re creating an iPhone or MacOS X desktop app named “MyGreatApp” the Info.plist file will be MyGreatApp-Info.plist
In your Xcode project open the Resources group and find the Info.plist file and double-click on it.
You should see a display similar to this:
This is the generic out-of the box version of the plist file; to this we need to add a short version string identifier (the marketing version)that the agvtool will increment at our request. To to this, click on any line in the current list and click the “+” symbol a the end of the line which will add a new entry. A pulldown menu will become available, and select “Bundle versions string, short” and set its value to “1.0″. The result should look like this:
To support automated build number, date and versioning will need to add 4 other plist items to this list:
- Add a “Versioning System” item, and set its value to “apple-generic”
- Add a “Current Project Version” item an set its initial value to 1
- Add a “CFBuildNumber” entry, set its initial value to zero
- Add a “CFBuildDate” entry, you can leave its initial value blank
To add values to a plist, click on the last item in the list, a plus-sign will appear on the right of the row, click on it and a new row will by added.
Once the new row exists, click on its title and a drop-down menu will appear from which you can select the new type of entry. You can also type the name of one of the entries above Xcode will do the right thing. Pressing the TAB key will take you to the content area where you can enter the values for the new field.
Save this plist file and repeat the process for any other targets whose version/build numbers you want to maintain.
Scripting the build number/date
Automating the build number and build date is accomplished by the addition on a new build phase to your target. Application targets typically have 3 build hases:
- Copy Bundle Resources
- Compile Sources
- Link Binary with Libraries
These are pretty self-explanatory; the new build phase we’re going add is a “script” phase that will allow us to run a BASH shell script that will be operating on the Plist we want to update. To add this new build phasae, right-click on your target’s name (in our example its the MyGreatApp target) and select NewRun Script Phase from the sub-menu:
You can call it whatever you like, I call mine “Update Build Number” just to its clear.
Next, you will want to double-click on the new script phase line and add the following to the script content area:
# Auto Increment Version Script buildPlist=${INFOPLIST_FILE} CFBuildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBuildNumber" $buildPlist) CFBuildNumber=$(($CFBuildNumber + 1)) /usr/libexec/PlistBuddy -c "Set :CFBuildNumber $CFBuildNumber" $buildPlist CFBuildDate=$(date) /usr/libexec/PlistBuddy -c "Set :CFBuildDate $CFBuildDate" $buildPlist |
Make sure that the “Shell” is set to /bin/bash and save the script.
Your target is now all set to have its build number and build date set set automatically. The nice thing about this method that it’s internal to Xcode from the point of view of file modifications; there are other ways to change info in the Info.plist but they often will leave you and your SCM at odds if you do auto-checkins on build because the plist file will be modified after the SCM checkin-has occurred.
If you want see it in action, simply build your project. Once the build is complete, look at the Info.plist file and you’ll see something like this:
Of course, since these values are in your app’s bundle you can easily get them programmatically:
NSString *appVersionNumber = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; NSString *buildNumber = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBuildNumber"]; NSString *buildDateString = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBuildDate"]; NSDate *buildDate = [dateFormatter dateFromString:buildDateString]; |
for use in your about screen, as part of info needed to talk to your back end servers, etc…
What About the “Marketing Version” Number?
The typically user visible marketing version number is something you’ll only increment when you are making a new release that will, for example, be uploaded to the AppStore and delivered to your customers. You don’t want to do it every tim you build, or very quickly you’ll go from a “version 1.0″ to a “Version 1517.7″ … which is certainly not what you want. So, when it does come time to bump this version number you’ll use avgtool.
Apple’s agvtool is typical Unix application, it takes all its instructions on what to do in the form of command line arguments. Like most command line programs it also has built-in help that can be displayed by simply runing gh program without any arguemnt, for example:
locahost% agvtool
Operation specifier required.
agvtool - Apple-generic versioning tool for Xcode projects
usage:
agvtool help
agvtool what-version | vers [-terse]
:
:
<lots of command line options omitted>
Incrementing the version number is as easy as the command agvtool next-version
The only thing you need to remember is to use this tool when the Xcode project is closed, or your changes could be lost. And, of course you will need to check this change into you SCM as well since unlike the build number/date updating above this has to happen outside of any internal Xcode scripting.
loading...


January 13, 2011 











Comments are closed.