Destroy Xcode Tedium
September 19th, 2005If you’re developing applications on the Mac, then you’re either familiar with, or about to become familiar with Xcode, Apple’s free development environment. I have been using Xcode for about five years now, if you count the time I spent with its predecessor, Project Builder. I am more or less happy with it, though of course I have my complaints.
Among the best changes in Xcode over the past few years has been increasingly improved scriptability, which means that the minor complaints one might have can often be remedied through a bit of automation. One of the things that has always bothered me about developing applications on the Mac is making certain that, with each version change, the correct values propagate to all the required points in the project. This is one of the most tedious and error-prone “chores” that needs to be tended to on a repeated basis. It used to mean changing ‘STR#’ resources to match ‘vers’ resources, and so on and so forth. On Mac OS X and with Xcode, it’s been pretty much reduced to editing a couple Info.plist keyed values: CFBundleGetInfoString and CFBundleShortVersionString.
Two is a nice, low number. Easy to manage, but it still means that whenever anything changes, it has to change in two tediously manual steps. Wouldn’t it be nice if you could store the version string for your application in one spot and have it be guaranteed that it would automatically appear in those two Info.plist keys?
Thanks to Xcode’s build value string substitution and its AppleScript scriptability, you can not only achieve this goal, but you can do it with panache and flair! The following steps will explain one method for easily configuring your projects such that, for all eternity, you can simply and quickly change the version string to whatever you desire with the flick of the wrist or the pressing of a keyboard shortcut.
1. Define a build setting for your application’s version string.
This will serve as the “central point of change” for your application’s “logical version number.” Examples of what this value may be set to include “1.0” or “1.7b1”, or even “1.Booyeah”. It’s up to you – whatever you consider the “base” version string to be, which you would normally go in and manually type into the two Info.plist keys mentioned above, you will now set as a separate, custom build setting. I named mine “APPLICATION_VERSION” and added it to the project’s build settings for “All Configurations.” Click the Top-Level project icon in Xcode, open the “Info” inspector, and open the “Build” pane. Now click the plus-sign icon to add a new setting, and type whatever name and initial version string you desire (or leave it bogus for now to prove that the script described below is working).
2. Replace hard-coded version strings with build-setting substitution markers.
Now that you’ve given Xcode an “APPLICATION_VERSION” or otherwise named build setting to store your version information inside of, you can replace any hard-coded version strings in your Info.plist file with the appropriate variable-expansion shorthand. For example, in my FastScripts project, I include the following declarations for the two Info.plist keys mentioned above:
<key>CFBundleGetInfoString</key> <string>FastScripts ${APPLICATION_VERSION}, ©2003-2005 Red Sweater Software</string> <key>CFBundleShortVersionString</key> <string>${APPLICATION_VERSION}</string>
(Note that because of a shortcoming in Xcode, this type of variable substitution will not be performed on your InfoPlist.strings file. This is a pretty good argument for moving these keys to the Info-MyProductName.plist file, though you will lose the ability to localize these keys in the process.)
You should now be able to build and run your application, open the “About Box” and observe the version strings were correctly substituted into your Info.plist keys. Go ahead, change the APPLICATION_VERSION build setting to something else, build and run again. Wasn’t that easy? It is easy, but not quite easy enough for my tastes.
3. Automate setting the APPLICATION_VERSION build setting.
It’s less tedious, but still annoying to have to go open the project inspector, locate the APPLICATION_VERSION build setting, and change it manually every time you want to release a newly versioned edition of your product. To make this truly as easy as developing on a Mac should be, we want to take advantage of Xcode’s AppleScript support to essentially add the “Change Application Version” dialog to Xcode. The following script accomplishes this. It asks the user for a new version string, and then iterates through all build configurations that advertise an APPLICATION_VERSION setting, changing the setting to the newly specified value.
Copy and paste the script below into your Script Editor, and run it. You will be prompted for a new string which, when the script finishes, should be set as the value of the APPLICATION_VERSION setting.
tell application "Xcode" set targetProj to project of active project document set newVersion to text returned of (display dialog "Enter the new Application Version value:" default answer "") -- For any build configuration that has an APPLICATION_VERSION build setting, -- change it to the new value. repeat with thisConf in build configurations of targetProj try set thisVersSetting to build setting "APPLICATION_VERSION" of thisConf set value of thisVersSetting to newVersion end try end repeat end tell
Fantastic, isn’t it? Of course, nobody wants to fire up Script Editor just to run a script that modifies a build setting in Xcode. That’s what the Script Menu (or the BMW of script menus, FastScripts) is for. From FastScripts, you can even assign an Xcode-specific keyboard shortcut, putting the functionality at your fingertips.
I’ve gotten in the habit of setting up all of my projects with an “APPLICATION_VERSION” build setting in this manner. I drop the above script into my Xcode application-specific scripts folder, and use FastScripts to configure Ctrl-V as an Xcode-specific trigger for running it. This essentially extends Xcode’s functionality for all of my projects such that changing the version on any of my projects is as easy typing Ctrl-V and typing the new string.
I will never have to worry about mismatching version strings again, and I can spit out a newly versioned edition with nary a whimper of tedium. Isn’t silence beautiful?
Update Now that you’ve got your marketing version in order, why not integrate Subversion’s revision number into your bundle version?
December 21st, 2005 at 1:16 pm
Why don’t you just use plist pre-processing, which was added to Xcode recently? Works for me.
December 22nd, 2005 at 7:44 am
This is pretty cool, but why not just use agvtool with the -all flag? Sure, you have to drop to the command line unless you want to create an AppleScript wrapper, but agvtool, being a supported product specifically designed for assigning, incrementing, and changing XCode project and marketing versions, it seems like in the long run, using agvtool as the basis for automating this process is probably a good idea.
I’m a little hesitant to add a new build setting to accomplish something that there are already build settings for.
Just my 2ยข, though, and this probably works fine for you.
December 22nd, 2005 at 8:17 am
Patrick: I guess I’m not sure exactly what I’d gain by using the preprocessor. I can already do what I need (substitute a build setting for a token) with the solution described here. Adding full preprocessing step seems like an unnecessary complication.
Jeff: Does the agvtool allow you to specify a marketing version in build settings? It seems to be much more focused on “build versions,” for which I prefer to use a home-grown Subversion-based solution.
December 24th, 2005 at 3:11 pm
Daniel:
Yeah, it actually does let you bump or set either the build or marketing version, or both. Historically, agvtool has not been the greatest, most stable piece of software to ever come out of Cupertino, but the current version works okay, even with Xcode open (Project Builder didn’t like when you used agvtool while it was up and running). This functionality should be integrated into XCode a little better though, so I’ll give your solution an edge in that regard – I like the command line, but I don’t want to have to use it.
Personally, I still haven’t switched over to Subversion using Xcode yet, though I do use it for my Eclipse projects. Must add it to my list of things I don’t have time to do. :)
August 21st, 2006 at 7:42 pm
[…] Centralizes the marketing version in one build setting called “APPLICATION_VERSION.” Just change this build setting to easily update your release version. […]
March 31st, 2007 at 11:23 pm
Hey Daniel, nice bit of AppleScript work here.
I just thought I would mention that there is already a build setting in Xcode that does exactly what your “APPLICATION_VERSION” variable is doing. It’s called “CURRENT_PROJECT_VERSION” and it’s not set by default so you can just co-opt it for your script in new projects.
I guess it doesn’t save you much time though. Setting an existing variable versus creating and setting a new one.
March 31st, 2007 at 11:27 pm
Thanks, Kevin. I didn’t know about that build setting. But after taking a quick look at it I learn that it’s intended to be a numeric-only value. My value is a string that is intended to be inserted anywhere in a string that the “marketing version” should go. For instance, it helps that I can release builds with APPLICATION_VERSION set to something like “1.0b1”.
March 31st, 2007 at 11:52 pm
I believe that is only true if you’ll be accessing it with agvtool. Xcode doesn’t seem to care what I put in there. Better safe than sorry though eh!
March 31st, 2007 at 11:54 pm
I guess if I’m going to use the variable for something different I might as well do Xcode the favor of a new name :)