† According to the Apple Documentation these versions can contain any characters. This is fantastic news if your working in an environment where you have several long-running features that need to be developed or tested at the same time; you can reflect this in the Build, like: 1.2.3-myfeature45 and 1.2.3-myotherfeature3 .

Unfortunately not. This is because iTunes Connect does version comparisons on uploaded builds and will reject anything that actually sits outside of the dot-separated-integer build numbers. Although this was very hard to debug because it doesn’t provide you with this information in the error response. Damn.

Let’s Play It Apple’s Way

So let’s just pretend that I didn’t spend a couple of hours trying every combination of versions to find out what iTunes Connect will or won’t allow me to do and just assume that I want to use the most common Apple versioning scheme. The one described above.

First, we should automatically increment the Build with each actual build. Makes sense, right? To do this we use shell scripts attached to the build process.

Open up the scheme for editing (this is the build configuration). Edit Scheme…in the image to the left.

Important: If you are sharing this project with others (via source control) or running it on any external computer (including a CI system) you must check the box that saves your Scheme with the project otherwise other copies of this project will not include these scripts.

Click on Build > Post-actions.

We want to increment the build number after the build because we’d rather not increment the build if it wasn’t successful — such as a compile error.

Click on the +button at the bottom and add a New Run Script Action.

Important:Xcode will not fail your build if this script fails (it will ignore the exit status of anyscripts we attach to the scheme). Furthermore all errors and standard out will be totally suppressed so debugging these scripts can be near impossible.

Using the following code:

PLIST="${PROJECT_DIR}/${INFOPLIST_FILE}"

PLB=/usr/libexec/PlistBuddy

LAST_NUMBER=$($PLB -c "Print CFBundleVersion" "$PLIST")

NEW_VERSION=$(($LAST_NUMBER + 1))

$PLB -c "Set :CFBundleVersion $NEW_VERSION" "$PLIST"

Important: Make sure you select your target above (by default it may not be selected) which will also break your script and you will have no idea why.

Save your configuration and do a few successful builds and you should see your Build incrementing. If so, proceed.

Taking It One Step Further

To have the Build number increment automatically is nice but that’s only one of the two version numbers we need to increase. It would be great if the patch was increased ( 1.2.3 -> 1.2.4 ) automatically when an Archive build is made.

Well, I’m sorry to say the similar technique above does not work for Archive builds as you would expect. Not because the version is in a different format, I had some fancy bash to handle that ‡, but because the time at which the build process captures the current version and when it increments it can never be done in a way that:

The patch is incremented. The Archive build contains the new patch version (so it’s correct). We can tag the correct patch version in git. I’ll explain this next.

I’m happy to be proven wrong. Perhaps I missed a combination that just works — please let me know if you figure it out.

So… I guess the cat’s out of the bag. I mentioned git tagging. Not only because it’s a good idea but if you have many concurrent features being developed it’s still hard to match up versions and build numbers with what that Archive actually contains.

The easiest and most reliable way is to tag the commit responsible for the build with the build number and/or version. But who want’s to do all that work? Well, your computer does.

Using the same process above to go into edit the scheme and we will add a new script to Archive > Pre-process:

PLIST="${PROJECT_DIR}/${INFOPLIST_FILE}"

PLB=/usr/libexec/PlistBuddy

LAST_NUMBER=$($PLB -c "Print CFBundleVersion" "$PLIST")

NEW_VERSION=$(($LAST_NUMBER - 1))

$PLB -c "Set :CFBundleVersion $NEW_VERSION" "$PLIST"

The main point of this script is to undo the build number caused by the previous Build phase so that when we do an Archive build the number used in the archive is not out of whack with the correct build number in the version control (and the soon to be tag).

Another script is added to the Archive > Post-process:

SHORT_VERSION=$($PLB -c "Print CFBundleShortVersionString" "$PLIST")

BUILD_NUMBER=$($PLB -c "Print CFBundleVersion" "$PLIST") # Tag version.

cd ${PROJECT_DIR}

git tag v$SHORT_VERSION-build$BUILD_NUMBER

The git tag will look something like v1.2.3-build456 . Feel free to customise this however you like.

The git tag will not be pushed to a central repo automatically. I left that out because it would make the archive much slower, cause it to depend on an internet connection and not everyones local git client is configured with all the right credentials.

Important: Make sure you select the target on both scripts. I can’t stress this enough. If you don’t, it just plain won’t work and you’l be ripping your hair out trying to work out why.