Pages

Tuesday, September 28, 2010

Scripting the Current Version into the Settings.bundle

This one is so simple! Thanks to some other posts (Stack Overflow, Dave DeLong), I've figured out how to programmatically set the current version number in my app's Settings.bundle/Root.plist file.


Let's say you have a newly-created Settings.bundle and you add a PSTitleValueSpecifier (see Item 1 in the .plist file) to display the current version number so that it looks like this:


...or like this...


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>StringsTable</key>
<string>Root</string>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>Group</string>
<key>FooterTitle</key>
<string>This is my app. You're welcome to use it as much as you want.</string>
</dict>
<dict>
<key>Type</key>
<string>PSTitleValueSpecifier</string>
<key>Title</key>
<string>Version</string>
<key>Key</key>
<string>CurrentVersionOfMyApp</string>
<key>DefaultValue</key>
<string>Nothing</string>
</dict>
<dict>
<key>Type</key>
<string>PSTextFieldSpecifier</string>
<key>Title</key>
<string>Name</string>
<key>Key</key>
<string>name_preference</string>
<key>DefaultValue</key>
<string></string>
<key>IsSecure</key>
<false/>
<key>KeyboardType</key>
<string>Alphabet</string>
<key>AutocapitalizationType</key>
<string>None</string>
<key>AutocorrectionType</key>
<string>No</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Enabled</string>
<key>Key</key>
<string>enabled_preference</string>
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSSliderSpecifier</string>
<key>Key</key>
<string>slider_preference</string>
<key>DefaultValue</key>
<real>0.5</real>
<key>MinimumValue</key>
<integer>0</integer>
<key>MaximumValue</key>
<integer>1</integer>
<key>MinimumValueImage</key>
<string></string>
<key>MaximumValueImage</key>
<string></string>
</dict>
</array>
</dict>
</plist>


Then, create a new Run Script in your app's target. You can do this by right-clicking on the specific target. In this case, it is MyApp.


When you do this, Xcode will add a new Run Script to your target as the last item. Also, Xcode will bring up the dialog window for the run script.

Set the Shell value to /bin/bash.

In the Script text field, add the following code:


#!/bin/bash
versionNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" MyApp-Info.plist)
/usr/libexec/PlistBuddy -c "Set :PreferenceSpecifiers:1:DefaultValue $versionNumber" Settings.bundle/Root.plist

***NOTE: The code above contains the name of "MyApp" in the name of the Info.plist file that the version number is read from. Be sure to change this to match your app's name.


Close the run script dialog window.

Remember back to where I mentioned that Xcode would add the run script as the last item in the target? Well, that's a problem. You see, the Root.plist file is changed to a binary format in the Link Binary With Libraries step in the target and any further changes to that Root.plist file will go unnoticed. So reposition the new Run Script step before that step or even as the first step in your target.

Go ahead and Build and Run you application in the simulator (or on your iPhone). As soon as your app launches, you can quit it and go to the Settings app. You should see the preferences entry for your application. If you touch that row, it will open up your app and there will be the current version number!

Easy, right? What a no-maintenance solution!