Home > iPhone > Adding SVN Revision Numbers to Your Application’s Version

Adding SVN Revision Numbers to Your Application’s Version

Recently, I’ve been given an oppotunity at my employer to participate in the next great version of our iPhone application. While it isn’t something fun and exciting, like ConvertBot or a game such as Eliminate Pro, it does allow me to get my hands dirty in Objective-C and iPhone development. With this experience has come a few things that I found difficult, at best, to spot an answer to within a single location. One of these things is something that, within a large corporation such as the one I work in, will help your support staff determine what version of your application a user currently has. Yes, there are items such as CFBundleVersion and CFBundleShortVersionString that accomplish this for you, but sometimes those may not be enough. Wouldn’t it be great if you could put the most recent commit revision number from your code repository? Good news, you can!

Setting up your info.plist file

Example info.plist file

Example info.plist file

First thing that you’ll need to do is set up the info.plist file in your project to carry the appropriate keys you’re going to need for your project. These are your CFBundleVersion and CFBundleShortVersionString, or, if you prefer to use the interface provided within XCode, ‘Bundle version’ and ‘Bundle version string, short’. These are used by Apple and iTunes to let the users know which version of the application you are using. From some simple tests I have done, they appear to check for the ‘Bundle version string, short’ key if it exists, and, if not, then fall back to the ‘Bundle version’. For our purposes, we know we do not want the build number shown within the App Store and iTunes, so we need to set up both of these variables.

Since the ‘Bundle version’ is going to change at build time to the latest commit revision, you can set this variable to anything you’d like. I prefer setting it to the same number as what will go in the ‘Bundle version string, short’ value. This ensures that, no matter what, an appropriate version number is given to the user.

Creating the Run Script

Target Build Steps

Target Build Steps

Now that the base is established, it’s time to move to getting the revision number added at build time. Why at build time do you ask? Basically, if this was added at any time other than during the build, it would put you into a loop where your application’s revision number is always one commit behind. If the update was done at any other time, you would run into the issue of changing info.plist to be the most recent commit statement, then having a sync to get this code into SVN, thus putting you a commit behind. Repeat ad nauseum until your eyes cross and you give up on the effort altogether.

This is where Apple has stepped in and given you an extremely powerful tool within the build process which allows you to create new Build Phases within your project. During the build of your project, you can execute code in multiple languages including, the Bash Shell, Ruby, Perl and Python, just to name a few. Now, your builds can add additional features such as this revision number by simply adding in a script in the right order in your build.

Example shot of the New Run Script Build Phase

Example shot of the New Run Script Build Phase

To do this, you have a few ways to do this. Within XCode, you can choose Project, New Build Phase, New Run Build Script Phase or from the Target in Groups and Files, CTRL+click on your application, choose Add, New Build Phase, New Run Build Script Phase. This will open a window to allow you to write your script in.

Now, for the fun part. In the language of your choosing, write your code. For this post, I’m going to use a Bash Shell to add the code necessary for the addition of our SVN Revision number. What we’re going to do is accomplished in only three lines of code, which, as anyone can agree, is awesome! The down side is that the code isn’t the most readable unless you’re very familiar with SED scripting.

REVISION=`svnversion -nc | /usr/bin/sed -e 's/^[^:]*://;s/[A-Za-z]//'`
APPVERSION=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${TARGET_BUILD_DIR}"/${INFOPLIST_PATH}`
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $APPVERSION ($REVISION)" "${TARGET_BUILD_DIR}"/${INFOPLIST_PATH}

Now, let’s explain what we’ve done. In the first line, we’re getting the SVN Revision number for the project. This is accomplished by using the svnversion program with two flags set, -n and -c. The -n flag removes the new-line character from the response (something we really don’t want anyway) and the -c flag asks for the most recent commit for the specific project you’re running under. We pipe that information into SED and run a quick regular expression on it. Why do we need a RegExp? Well, svnversion -c returns more information than we really need. It sends us back the initial version from when project was created followed by the last committed version. For instance, if you created the project and it was assigned version 500, three weeks later you’ve had quite a bit of code activity and your final check-in gives you revision number 847. When you run svnversion -c within your project folder, you will get back a result of 500:847. That’s great, but within you’re application you’re only looking for the last number. That’s where the SED command comes in.

The next line is something I’ve done in my project, but is, obviously not something that has to be done. Within that code is something important, it’s a call to an application that comes with XCode called PlistBuddy. PlistBuddy, in short, is a tool to read and update plist files which is exactly what we’re looking to do. The call is simply to store the value of CFBundleShortVersionString from our info.plist file to a local variable in the shell script. Since some developers (eh-hem, @chadpredovich) like to create their projects within a directory structure with spaces in it, the ${TARGET_BUILD_DIR} is wrapped in quotes to ensure the script doesn’t fail. Of course, TARGE_BUILD_DIR and INFOPLIST_PATH are variables within the build wrapper that is executing this script, so they’re available to us as well. Nice, huh?

The final line again uses PlistBuddy, but this time to set the CFBundleVersion to the values stored in our two variables, APPVERSION and REVISION.

Move your Run Script to ensure updates to info.plist occur before the build of the application

Now that you have the script set up, simply place it in the correct order within the Build Phases. In our case, we want to do this immediately after the ‘Copy Bundle Resources’ step inside your Target as the image illustrates.

Time to add the pretty little bow

NSString *version = [@"Version " stringByAppendingString:[mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]];

Now that you have the revision number added into the info.plist file, it’s as simple as pulling that information back out where you want to show it in your application. A little Obj-C magic and your support staff can rest easy that they can easily reproduce the issue a user may be having within your application by ensuring they are building the code from the exact code base that went to Apple.

Update!!! (2010/01/12)

When we tried to submit our app to the Apple App Store for review, it was rejected due to an improperly formed CFBundleVersion. It turns out, and is consequently poorly documented, that the CFBundleVersion can only be in the following format NN.N.N Where N is a number from 0 to 9. As you can tell, this means that in an application versioning, the app’s major version is a number between 0 and 99, minor and revision between 0 and 9. I’m not sure whether the version numbers are strictly upheld, but I can say that the above post will not be accepted.

Well, back to the drawing board on this one!

Categories: iPhone Tags: , , ,
  1. No comments yet.
  1. No trackbacks yet.

Twitter links powered by Tweet This v1.8.3, a WordPress plugin for Twitter.