Why DevMate is great, and how to correctly version your app there

Jul 09, 2015

Have you heard about DevMate? If you develop apps on Mac, you should check it out.

They’ve been around for a while, but I just found about them since they’re running a big campaign on the blogs that I read. (So yes, as much as I’d like to claim I never follow advertising, in this case, it was well targeted and it worked great.)

If you develop apps on any platform, there’s a number of supporting tasks that you have to do, to actually get your app into the hands of your users, and to support and maintain it over time: deliver updates, collect metrics, report crashes, sell licenses. On mobile platforms, many of these are handled by the relevant app stores, though even there, there’s space for innovation as shown by Testflight, HockeyApp etc.

On Mac, you also have the App Store, but for various reasons, its developer experience is not so great, and luckily, distributing apps outside the app store continues to be fully supported on the Mac. Now, to handle the tasks listed above, there are a great many services (Hockey, Testflight, Crashlytics, Localytics, Paddle, Sparkle etc), all requiring various forms of integration and hosting.

What DevMate does is basically tie all of these features into a nice single library and hosting platform, so that you can just use them, instead of all the separate ones. It does internally use some of the listed pieces, but provides extra value on top. For example, Sparkle today does not support updating sandboxed apps out of box, requiring some hacking to get this to work. Well, DevMate has done this hacking for you and provides a ready-to-use update component based on Sparkle that also supports sandboxed apps. Fantastic.

Plus, their entry-level plan is free, unlike, say, Hockey’s. I’m hooked after trying it out, and plan to use this in all my Mac apps going forward.

Version numbers

The one thing I wanted to talk about a bit more is how to version your app with DevMate, so that updates work correctly. This continues to be a source of frustration on both Mac and iOS for new developers, and the tool UI-s, documentation and unfortunately also the DevMate UI do not make this particularly clear.

The discussion below applies to DevMate, but also to Sparkle more generally.

Let’s look at what we’re working with. There are two fields in your app’s Info.plist file driving the version numbering, called CFBundleVersion and CFBundleShortVersionString. Here’s how they look like in Xcode.

Yes, it is confusing that the thing that’s called “Version” in Xcode, is not the one called “CFBundleVersion” in Info.plist. Just live with it.

If you were to build and run an app with these settings, here’s how the default About box of the app looks.

Let’s look at what Apple docs say about them.

CFBundleVersion (String - iOS, OS X) specifies the build version number of the bundle, which identifies an iteration (released or unreleased) of the bundle. The build version number should be a string comprised of three non-negative, period-separated integers with the first integer being greater than zero. The string should only contain numeric (0-9) and period (.) characters. Leading zeros are truncated from each integer and will be ignored (that is, 1.02.3 is equivalent to 1.2.3). This key is not localizable.

CFBundleShortVersionString (String - iOS, OS X) specifies the release version number of the bundle, which identifies a released iteration of the app. The release version number is a string comprised of three period-separated integers. The first integer represents major revisions to the app, such as revisions that implement new features or major changes. The second integer denotes revisions that implement less prominent features. The third integer represents maintenance releases.

Well… that’s wordy, and not all that helpful. It basically says that for released versions of the app, these numbers should be the same.

Here’s how I’ve began to think about these numbers, which also matches 1) how Sparkle uses them, 2) what the default Xcode templates provide you, and 3) what the App Store requires. CFBundleVersion, also called “Build” in Xcode UI, should be the build sequence number. It should be one integer that is always increasing. This makes Sparkle updates work great because Sparkle uses comparison between these numbers to determine which build is newer. If you use dotted integers here, Sparkle simply stopped working for me.

There’s different ways to manage CFBundleVersion as an ever-increasing integer. You can manually bump it in Info.plist once in a while, or you can tie it to your git commits, or you can have your CI server bump this every time a build is made. (And if builds are triggered by commits, these two will be the same thing.)

So, CFBundleVersion: one integer that only ever increases, and everything is great.

Now, CFBundleShortVersionString should be the thing that’s commonly thought of as version number. Something like “1.0.4”. Use semantic versioning if you’d like.

So, if you follow the above rules in a Sparkle-based system, you’ll be fine. How to do this in DevMate, though?

Well, here’s how I do it, and I tested that this approach gives me the desired results. When I go to add a version, I click through to the “Advanced” tab and set both version numbers as shown.

The link under the “Bundle Version” field takes you to this document which basically tells you about semantic versioning, and even tells you can add release qualifiers like "a", "b" to this number. This is problematic, and Sparkle stopped working for me when I used semver with Bundle Version. Just do the integer thing as shown, and use the dotted semantic numbers in the “Short Bundle Version” field.

Now, if you follow this scheme, here’s what your Release Management view will end up looking like.

It’s weird to see Version # written like “22 (1.0.5)” where the About box with your app will instead say “1.0.5 (22)” as shown above. But remember, this is the developer view, not the user view. When you do this, everything will work great for your users.