Auto-Increment Build Numbers for Multiple Xcode Target Apps

3 minute read

Recently, I launched an App called NYC Bikes. Because it quickly became pretty popular, I expanded on it and created another bike share app for Chicago. I then created apps for London, Washington D.C., Melbourne, and San Francisco (all currently under Apple review) with more on the way.

These apps share the same UI, 99% of the same code base, with only API endpoints, colors, strings and images swapped out. I previously wrote about my fix for using Xcode asset catalogs with multiple targets, so I thought I would also share my build number strategy for all of these bike share apps.

If you have been making iOS apps for a while then you probably already have a Xcode run script to auto-increment your CFBundleVersion. It probably reads the Info.plist, adds a num, then writes it back. Something like this:

# Auto Increment Version Script
buildPlist='YourApp Info.plist'
CFBundleVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$buildPlist")
CFBundleVersionNew=$(($CFBundleVersion + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $CFBundleVersionNew" "$buildPlist"
/usr/libexec/PlistBuddy -c "Set :CFBuildDate $CFBuildDate" "$buildPlist"
touch "$buildPlist"

This code is fine for one app and I have used it in several projects. But when making multiple targets of the same app like I have on the Bikes project, where each app is 99.9% similar to the others, it makes sense to keep the build versions the same.

This is so that in the wild, when looking at the different apps and build numbers, I can always be assured that the higher build number is the more recent build, even across different bike apps. If I were to increment each app's build number only when I built that specific project, I would get no information from just looking at the build numbers of two different apps without going back to Xcode and doing a manual lookup of what version was released when.

I will never have to remember which version of which app got the fix to bug X, only that bug X was fixed in build number Y. Then I know which apps in production have the fix applied to it just by looking at the build number.

Thus, I crafted a build run script that is on each target, before compilation.

# set new $IFS
IFS=$(echo -en "\n\b")
FILES="Bikes App/Info/*.plist"
for plist in $FILES
  echo "Updating $plist"

  if [[ -z "$CFBundleVersionNew" ]]
    CFBundleVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$plist")
    CFBundleVersionNew=$(($CFBundleVersion + 2))

  /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $CFBundleVersionNew" "$plist"


  /usr/libexec/PlistBuddy -c "Set :CFBuildDate $CFBuildDate" "$plist"

  touch "$plist"
# restore $IFS

In English, it looks at all the Info.plist files for the targets, calculates the new bundle version if I haven't already, then sets it on the plist along with the current date. The bash junk up top just tells the for loop to handle whitespace properly in the file list.

If you are paying attention you will also note that I increment the version by 2, not 1. This is because I particularly like odd-numbered build versions. It makes no logical sense, but I feel that even-numbered build numbers have the tendency to look fabricated. I would rather release a version that just happened to be 5001 rather than 5000, so I start new Apps at 1001 and auto-increment by two from there on every build. Enjoy!

Lastly, I run a small software company called Urban Apps. It pays the bills so I can take the time to write helpful posts like this. If you found this page helpful at all, I would really appreciate it if you would check out my Apps on the iTunes App Store.

Was this page helpful for you? Buy me a slice of 🍕 to say thanks!