My application uses the Cocoa Framework Sparkle to deploy updates. I don't usually deploy beta's of my software but for my next update, I feel like I need to. My question is what is the best numbering strategy for deploying a beta using Sparkle. For anyone who tests my beta I'd like the update to be seamless when I release the next official version, but for other users I'd like the whole system to be totally invisible. I currently use a numbering system like 1.2.3 for my updates.
The best way is probably to totally disconnect your CFBundleVersion (which must contain only . and numbers, and is used by Sparkle's version comparison and the OS), and CFBundleShortVersionString (which can by anything, and is what users see).
Then, you just have to make sure that your CFBundleVersion always increases over time, but can otherwise be anything[*], while you use 1.2.4b and 1.2.4 as the CFBundleStortVersionString for the beta and final releases respectively. As long as the CFBundleVersion for the beta is higher that your current CFBundleVersion, and the CFBundleVersion of the eventual non-beta release is higher than the beta, everything will work out the way you want.
[*] Just keep in mind that despite Apple's docs not mentioning it, 9999.99.99 is pretty much the highest version that LaunchServices will recognize, and it will ignore any number blocks beyond the third, so plan to use a scheme that won't even go higher than that; Sparkle updates would still work, but the OS would be confused about which copy was the latest version.
I like to use the Apple versioning tool included with Xcode. It maintains a parallel build number (e.g. 12345) that is distinct from your marketing version number (1.2.3). You invoke it using the command line tool agvtool
.
What's more, if you're using Subversion or CVS as your versioning system, this tool has built-in support. For example, which I want to increment my build number, I just enter this in the terminal:
agvtool -usesvn bump -all
This increments the build number of every target in my application, updates the Info.plist
files, and then commits the whole thing to SVN automatically. There's also a new-marketing-version
verb you can use to set the CFBundleShortVersionString
across all your project's targets. Take a look at the man page for agvtool
(i.e. type man agvtool
at the terminal) for more details.
So what does this have to do with Sparkle? I use the build number as my sparkle:version
number. Using the build number makes it dead simple for Sparkle to figure out whether it's the current version or not. For the users' benefit, I like to put the build number right in the marketing version number. So my beta version numbers look something like this: 1.2.3 (456)
. Apple does something very similar with Safari. If I go Safari > About Safari right now, I see version 4.0.2 (5530.19).
I've recently looked into doing this too. The development setup for my app is Xcode (obviously) with Sparkle, and I maintain my code in a Mercurial repository. As part of my build process, I query Mercurial using the "hg id" to populate the Info.plit. This is done in a build script for my Xcode target. This is the script:
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion `/usr/local/bin/hg id -in`" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString `/usr/local/bin/hg id -t`" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
So, for beta releases, I can just tag my changeset as "0.29b" or whatever. To make it so that users who want to get beta releases I implement the SUUpdater delegate method:
#pragma mark -
#pragma mark SUUpdate Delegate methods
- (NSArray *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile {
if([[NSUserDefaults standardUserDefaults] boolForKey:BSEnableBetaUpdates]) {
return [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:@"beta", @"key", [NSNumber numberWithBool:YES], @"value", @"Enable beta updates", @"displayKey", @"Yes", @"displayValue", nil], nil];
} else {
return nil;
}
}
Where BSEnableBetaUpdates is a constant that get set by the user in my preferences window. What this does is make sure that the GET request to your feed url contains beta=1. On the server you can interpret this and provide an appcast of beta releases or if it doesn't exist of normal releases. I wont explain how you could do that, either using php, .htaccess whatever.