{"id":794,"date":"2014-11-15T13:20:17","date_gmt":"2014-11-15T19:20:17","guid":{"rendered":"http:\/\/dev.iachieved.it\/iachievedit\/?p=794"},"modified":"2014-11-15T13:20:17","modified_gmt":"2014-11-15T19:20:17","slug":"a-technique-for-displaying-ios-build-versions-in-swift","status":"publish","type":"post","link":"https:\/\/dev.iachieved.it\/iachievedit\/a-technique-for-displaying-ios-build-versions-in-swift\/","title":{"rendered":"A Technique for Displaying iOS Build Versions (in Swift)"},"content":{"rendered":"<p>It is a universally recognized best practice to visibly expose your iOS application&#8217;s version number somewhere on the screen.  Often times you will see it on the application&#8217;s splash screen or can get to it by selecting the <i>About<\/i> menu item (assuming the application has a menu).  No matter where it&#8217;s location (I have my preference as to where it should be, which I will describe momentarily), it&#8217;s a good idea to have it easily viewed by both your beta testers and your users.<\/p>\n<p>To be clear, in this post we&#8217;re not going to talk about techniques for automatically incrementing the version or build numbers, or strategies for injecting <code>git commit-ish<\/code> 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 <code>CFShortVersionString<\/code> and <code>CFBundleVersion<\/code> and how you can combine the two to provide a straightforward and adequate version number for your application.<\/p>\n<p>I have always used the <i>major.minor.build<\/i> version number format, where <i>major.minor<\/i> are typically referred to as <i>marketing versions<\/i>, because they are heavily influenced by marketing departments and their desire to articulate to customers &#8220;how much new content&#8221; is in a given version compared to a previous one.  For example, it&#8217;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&#8217;s also pretty safe to assume that version 1.1 is not radically different from version 1.0.  <\/p>\n<p>So we can agree that the marketing and product management teams are walking around talking about version <i>major.minor<\/i>, and that&#8217;s fine.  For the purposes of distributing and tracking versions of our application, however, we need one more field, the <i>build<\/i> 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 &#8220;ideally&#8221; but in reality the <code>CFBundleVersion<\/code>, which we will use as our <i>build<\/i> identifier, must be incremented when posting a new version to iTunesConnect).<\/p>\n<p>It turns out that our Xcode target comes with <i>Identity<\/i> settings such as <b>Bundle Identifier<\/b>, <b>Version<\/b>, and <b>Build<\/b>.  We will leverage those settings, and in particular refer to the combination of <b>Version<\/b> and <b>Build<\/b> as our application version.<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/appversion_initial.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/appversion_initial.png\" alt=\"appversion_initial\" width=\"690\" height=\"196\" class=\"alignnone size-full wp-image-808\" \/><\/a><\/p>\n<p>The Xcode <b>Version<\/b> is prepopulated with <code>1.0<\/code> for a new project, and this will serve as our <code>major.minor<\/code> <i>marketing version<\/i>.  It also happens to be the version number that people will see on the iTunes store.  The Xcode <b>Build<\/b> version will be the <code>build<\/code> value in our <code>major.minor.build<\/code> tuple.  Again, let&#8217;s take a moment to stress that for this post we aren&#8217;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.<\/p>\n<p>Now that we know what values we want to treat as <code>major.minor.build<\/code> let&#8217;s look at how we can access them in our application.  It turns out these two values have names in our application&#8217;s <i>main bundle<\/i>, which is loaded from the contents of the applications <code>Info.plist<\/code>.  We can extract the values by referring to them by their names:  <code>CFBundleShortVersionString<\/code> and <code>CFBundleVersion<\/code>.  Here&#8217;s the Swift code for doing so, and it should be placed in your <code>application(application:didFinishLaunchingWithOptions:)<\/code> routine in the <code>AppDelegate<\/code> class.<\/p>\n<p>[objc]<br \/>\nlet appInfo = NSBundle.mainBundle().infoDictionary as Dictionary&lt;String,AnyObject&gt;<br \/>\nlet shortVersionString = appInfo[&quot;CFBundleShortVersionString&quot;] as String<br \/>\nlet bundleVersion      = appInfo[&quot;CFBundleVersion&quot;] as String<br \/>\n[\/objc]<\/p>\n<p><b>Note<\/b>:  We&#8217;re leveraging Swift&#8217;s <code>as<\/code> keyword, which allows us to specify that <code>appInfo<\/code> is a <code>Dictionary<\/code> of <code>AnyObject<\/code>s with keys of type <code>String<\/code>.  Furthermore, we apply <code>as<\/code> again to tell Swift that the objects we are retrieving for the keys <code>CFBundleShortVersionString<\/code> and <code>CFBundleVersion<\/code> are <code>String<\/code>s.  Don&#8217;t make the mistake in thinking that our <code>appInfo<\/code> dictionary consists of only of <code>String-String<\/code> key-value pairs.  Refer to <a href=\"https:\/\/developer.apple.com\/library\/ios\/documentation\/General\/Reference\/InfoPlistKeyReference\/Articles\/AboutInformationPropertyListFiles.html\">Apple&#8217;s documentation<\/a> for details of the types of values stored.<\/p>\n<p>We can combine the <code>CFBundleShortVersionString<\/code> and <code>CFBundleVersion<\/code> together as:<\/p>\n<p>[objc]<br \/>\nlet applicationVersion = shortVersionString + &quot;.&quot; + bundleVersion<br \/>\n[\/objc]<\/p>\n<p>Okay, now that we&#8217;ve established we can create a version string from <code>CFBundleShortVersionString<\/code> and <code>CFBundleVersion<\/code>, where should we display it?  I am a fan of placing it in the application&#8217;s <i>iOS Settings<\/i> 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 <a href=\"https:\/\/developer.apple.com\/library\/ios\/documentation\/Cocoa\/Conceptual\/UserDefaults\/Preferences\/Preferences.html\">Apple&#8217;s documentation<\/a>.<\/p>\n<p>To create a settings page you will need to create a <b>Settings Bundle<\/b> for your application.  In Xcode select <b>File &#8211; New &#8211; File<\/b> and when presented with choosing a template for your new file, select <b>iOS &#8211; Resource &#8211; Settings Bundle<\/b>, like so:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/add_settings_bundle.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/add_settings_bundle.png\" alt=\"add_settings_bundle\" width=\"733\" height=\"432\" class=\"alignnone size-full wp-image-807\" \/><\/a><\/p>\n<p>Select <b>Next<\/b> and ensure that your settings bundle is included in your application target (it should be by default).  Click <b>Create<\/b> and you will see Xcode has included <code>Settings.bundle<\/code> in your project (I also place my <code>Settings.bundle<\/code> in the <b>Supporting Files<\/b> group):<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/settingsbundle_added.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/settingsbundle_added.png\" alt=\"settingsbundle_added\" width=\"337\" height=\"289\" class=\"alignnone size-full wp-image-806\" \/><\/a><\/p>\n<p>We are interested in the file called <code>Root.plist<\/code>, where we will add a <b>Group<\/b> section called <b>About<\/b>, which will contain both our copyright line as well as version number.  Click on <code>Root.plist<\/code> to bring up the default contents:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/rootplist_default.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/rootplist_default.png\" alt=\"rootplist_default\" width=\"804\" height=\"180\" class=\"alignnone size-full wp-image-805\" \/><\/a><\/p>\n<p>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 <b>Cut<\/b>.  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 <b>Group<\/b>.  <\/p>\n<p>Click on the disclosure triangle for Item 0 and you will see <b>Title<\/b> and <b>Type<\/b>.  Leave the <b>Type<\/b> as it is (a <b>Group<\/b>), and change the title to <b>About<\/b>.  Your panel should look like this:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/rootplist_about_only.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/rootplist_about_only.png\" alt=\"rootplist_about_only\" width=\"803\" height=\"135\" class=\"alignnone size-full wp-image-804\" \/><\/a><\/p>\n<p>Now for the trickier part, and it&#8217;s only trickier because the Xcode plist editor can be a pain.  Click on the Preference Items row and right-click and select <b>Add Row<\/b>.  You will probably notice that our <b>Group<\/b> got moved to Item 1.  We don&#8217;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 <b>About<\/b> group is the first.  It should look something like this:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/rootplist_withabout_and_fields.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/rootplist_withabout_and_fields.png\" alt=\"rootplist_withabout_and_fields\" width=\"803\" height=\"128\" class=\"alignnone size-full wp-image-803\" \/><\/a><\/p>\n<p>Now, let&#8217;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 <b>Add Row<\/b>.  The default row should have <b>Default Value<\/b> as its type.  Put your copyright statement as the default value, change the title field to <b>Copyright<\/b>, and then for <b>Identifier<\/b> put <code>application_copyright<\/code>.<\/p>\n<p>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 <b>Add Row<\/b>.  Set the default value to 0.0.0, change the title field to <b>Version<\/b>, and then for <b>Identifier<\/b> put <code>application_version<\/code>.<\/p>\n<p>Your settings <code>Root.plist<\/code> should now look like<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/rootplist_completed.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/rootplist_completed.png\" alt=\"rootplist_completed\" width=\"803\" height=\"306\" class=\"alignnone size-full wp-image-802\" \/><\/a><\/p>\n<p>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 <i>actual<\/i> version number in there.  Here&#8217;s the code for doing so (again, place it after your extract the values from <code>appInfo<\/code> in your application delegate&#8217;s <code>application(application:didFinishLaunchingWithOptions)<\/code> routine):<\/p>\n<p>[objc]<br \/>\n    let defaults = NSUserDefaults.standardUserDefaults()<br \/>\n    defaults.setObject(applicationVersion, forKey: &quot;application_version&quot;)<br \/>\n    defaults.synchronize()<br \/>\n[\/objc]<\/p>\n<p><code>NSUserDefaults.standardUserDefaults()<\/code> gives us access to our application&#8217;s settings, to which we write our <code>applicationVersion<\/code> to the entry given by the key <code>application_version<\/code>.    <code>defaults.synchronize()<\/code> persists the updated information to storage such that other applications (such as iOS Settings) can retrieve the value.<\/p>\n<p>Run the application again, browse to the iOS Settings for the application, and find<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/appversion_final.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/appversion_final.png\" alt=\"appversion_final\" width=\"320\" height=\"568\" class=\"alignnone size-full wp-image-809\" \/><\/a><\/p>\n<p>To increment your <i>build<\/i> number, go to your project target and simply change it, rebuild the application, and see the new number available in the settings page.<\/p>\n<p>Again, we&#8217;ve tiptoed around the topic of how and when to update your build version.  In the past I&#8217;ve written elaborate scripts which ran under <a href=\"http:\/\/cruisecontrol.sourceforge.net\">CruiseControl<\/a>, extracted the SVN version number, injected that number into the <code>Info.plist<\/code> using <a href=\"https:\/\/developer.apple.com\/library\/mac\/documentation\/Darwin\/Reference\/ManPages\/man8\/PlistBuddy.8.html\">PListBuddy<\/a>, 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!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It is a universally recognized best practice to visibly expose your iOS application&#8217;s version number somewhere on the screen. Often times you will see it on the application&#8217;s splash screen or can get to it by selecting the About menu item (assuming the application has a menu). No matter where it&#8217;s location (I have my [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,2],"tags":[],"class_list":["post-794","post","type-post","status-publish","format-standard","hentry","category-swift","category-xcodetips"],"_links":{"self":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/794"}],"collection":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/comments?post=794"}],"version-history":[{"count":20,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/794\/revisions"}],"predecessor-version":[{"id":822,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/794\/revisions\/822"}],"wp:attachment":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media?parent=794"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/categories?post=794"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/tags?post=794"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}