A Technique for Displaying iOS Build Versions (in Swift)

Categories:

It is a universally recognized best practice to visibly expose your iOS application’s version number somewhere on the screen. Often times you will see it on the application’s splash screen or can get to it by selecting the About menu item (assuming the application has a menu). No matter where it’s location (I have my preference as to where it should be, which I will describe momentarily), it’s a good idea to have it easily viewed by both your beta testers and your users.

To be clear, in this post we’re not going to talk about techniques for automatically incrementing the version or build numbers, or strategies for injecting git commit-ish or branch identifiers into your application. There are plenty of resources on these topics on the Web. What we will discuss are the differences between CFShortVersionString and CFBundleVersion and how you can combine the two to provide a straightforward and adequate version number for your application.

I have always used the major.minor.build version number format, where major.minor are typically referred to as marketing versions, because they are heavily influenced by marketing departments and their desire to articulate to customers “how much new content” is in a given version compared to a previous one. For example, it’s pretty safe to assume that version 2.0 of some given software has a lot of new features compared to version 1.0. It’s also pretty safe to assume that version 1.1 is not radically different from version 1.0.

So we can agree that the marketing and product management teams are walking around talking about version major.minor, and that’s fine. For the purposes of distributing and tracking versions of our application, however, we need one more field, the build identifier. Ideally, this number should always be increasing, and no two different builds that are distributed to beta testers should have the same build number. (Note: we say “ideally” but in reality the CFBundleVersion, which we will use as our build identifier, must be incremented when posting a new version to iTunesConnect).

It turns out that our Xcode target comes with Identity settings such as Bundle Identifier, Version, and Build. We will leverage those settings, and in particular refer to the combination of Version and Build as our application version.

appversion_initial

The Xcode Version is prepopulated with 1.0 for a new project, and this will serve as our major.minor marketing version. It also happens to be the version number that people will see on the iTunes store. The Xcode Build version will be the build value in our major.minor.build tuple. Again, let’s take a moment to stress that for this post we aren’t going to talk about how to automatically increment that value and will increment it by hand every time we build a version that we want to distribute.

Now that we know what values we want to treat as major.minor.build let’s look at how we can access them in our application. It turns out these two values have names in our application’s main bundle, which is loaded from the contents of the applications Info.plist. We can extract the values by referring to them by their names: CFBundleShortVersionString and CFBundleVersion. Here’s the Swift code for doing so, and it should be placed in your application(application:didFinishLaunchingWithOptions:) routine in the AppDelegate class.

[objc]
let appInfo = NSBundle.mainBundle().infoDictionary as Dictionary<String,AnyObject>
let shortVersionString = appInfo["CFBundleShortVersionString"] as String
let bundleVersion = appInfo["CFBundleVersion"] as String
[/objc]

Note: We’re leveraging Swift’s as keyword, which allows us to specify that appInfo is a Dictionary of AnyObjects with keys of type String. Furthermore, we apply as again to tell Swift that the objects we are retrieving for the keys CFBundleShortVersionString and CFBundleVersion are Strings. Don’t make the mistake in thinking that our appInfo dictionary consists of only of String-String key-value pairs. Refer to Apple’s documentation for details of the types of values stored.

We can combine the CFBundleShortVersionString and CFBundleVersion together as:

[objc]
let applicationVersion = shortVersionString + "." + bundleVersion
[/objc]

Okay, now that we’ve established we can create a version string from CFBundleShortVersionString and CFBundleVersion, where should we display it? I am a fan of placing it in the application’s iOS Settings page. This is page created automatically by iOS for your application, and it can be found by going to the iOS Settings application and scrolling down to the bottom of the default page. For complete details of an application settings page, see Apple’s documentation.

To create a settings page you will need to create a Settings Bundle for your application. In Xcode select File – New – File and when presented with choosing a template for your new file, select iOS – Resource – Settings Bundle, like so:

add_settings_bundle

Select Next and ensure that your settings bundle is included in your application target (it should be by default). Click Create and you will see Xcode has included Settings.bundle in your project (I also place my Settings.bundle in the Supporting Files group):

settingsbundle_added

We are interested in the file called Root.plist, where we will add a Group section called About, which will contain both our copyright line as well as version number. Click on Root.plist to bring up the default contents:

rootplist_default

Delete Item 1, Item 2, and Item 3 by clicking on the row for the item to highlight it, and then right-click and choose Cut. When you delete one item the remaining items will renumber themselves, so simply ensure only one item remains, Item 0, which by default is a Group.

Click on the disclosure triangle for Item 0 and you will see Title and Type. Leave the Type as it is (a Group), and change the title to About. Your panel should look like this:

rootplist_about_only

Now for the trickier part, and it’s only trickier because the Xcode plist editor can be a pain. Click on the Preference Items row and right-click and select Add Row. You will probably notice that our Group got moved to Item 1. We don’t want that, so click and drag the Item 1 row back to be the first entry. Add another row, and again, rearrange the entries such that our About group is the first. It should look something like this:

rootplist_withabout_and_fields

Now, let’s set the two fields. The first will be our copyright statement. Click on the disclosure triangle for Item 1, and then right-click on Item 1 and Add Row. The default row should have Default Value as its type. Put your copyright statement as the default value, change the title field to Copyright, and then for Identifier put application_copyright.

The second field will be our application version number. Click on the disclosure triangle for Item 2, and then right-click on Item 2 and Add Row. Set the default value to 0.0.0, change the title field to Version, and then for Identifier put application_version.

Your settings Root.plist should now look like

rootplist_completed

If you ran the application as is and then navigated to the iOS Settings page for the application, you would see your copyright statement and 0.0.0 as the version number. Of course we want to get the actual version number in there. Here’s the code for doing so (again, place it after your extract the values from appInfo in your application delegate’s application(application:didFinishLaunchingWithOptions) routine):

[objc]
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(applicationVersion, forKey: "application_version")
defaults.synchronize()
[/objc]

NSUserDefaults.standardUserDefaults() gives us access to our application’s settings, to which we write our applicationVersion to the entry given by the key application_version. defaults.synchronize() persists the updated information to storage such that other applications (such as iOS Settings) can retrieve the value.

Run the application again, browse to the iOS Settings for the application, and find

appversion_final

To increment your build number, go to your project target and simply change it, rebuild the application, and see the new number available in the settings page.

Again, we’ve tiptoed around the topic of how and when to update your build version. In the past I’ve written elaborate scripts which ran under CruiseControl, extracted the SVN version number, injected that number into the Info.plist using PListBuddy, generated fancy build records that could be used independently to package other components together, etc. In the end I found that it was often more trouble than it was worth, and if you are running a development team with only a handful of folks, simply appoint one to be responsible for building the version that gets tested and eventually posted to Apple for review. They can either build the application by hand (using Xcode) or develop automated tools for it; the important thing is the information is unique for each build distributed and can easily be viewed!

Leave a Reply

Your email address will not be published. Required fields are marked *