680

I have an app that I developed with Xcode 3 and recently started editing with Xcode 4. In the target summary I have the iOS application target form with fields: identifier, version, build, devices, and deployment target. The version field is blank and the build field is 3.4.0 (which matches the version of the app from when I was still editing with Xcode 3).

My questions are:

  1. What is the difference between the version and build fields?

  2. Why was the version field blank after I upgraded to Xcode 4?

Cœur
  • 32,421
  • 21
  • 173
  • 232
chris
  • 15,435
  • 8
  • 34
  • 38
  • For one thing, I think it's the Build number that shows up in the Xcode Organizer archives list. Other than that, I'm not sure what it's used for. – Daniel Dickison Jul 28 '11 at 13:26

7 Answers7

1257

Apple sort of rearranged/repurposed the fields.

Going forward, if you look on the Info tab for your Application Target, you should use the "Bundle versions string, short" as your Version (e.g., 3.4.0) and "Bundle version" as your Build (e.g., 500 or 1A500). If you don't see them both, you can add them. Those will map to the proper Version and Build textboxes on the Summary tab; they are the same values.

When viewing the Info tab, if you right-click and select Show Raw Keys/Values, you'll see the actual names are CFBundleShortVersionString (Version) and CFBundleVersion (Build).

The Version is usually used how you appear to have been using it with Xcode 3. I'm not sure on what level you're asking about the Version/Build difference, so I'll answer it philosophically.

There are all sorts of schemes, but a popular one is:

{MajorVersion}.{MinorVersion}.{Revision}

  • Major version - Major changes, redesigns, and functionality changes
  • Minor version - Minor improvements, additions to functionality
  • Revision - A patch number for bug-fixes

Then the Build is used separately to indicate the total number of builds for a release or for the entire product lifetime.

Many developers start the Build number at 0, and every time they build they increase the number by one, increasing forever. In my projects, I have a script that automatically increases the build number every time I build. See instructions for that below.

  • Release 1.0.0 might be build 542. It took 542 builds to get to a 1.0.0 release.
  • Release 1.0.1 might be build 578.
  • Release 1.1.0 might be build 694.
  • Release 2.0.0 might be build 949.

Other developers, including Apple, have a Build number comprised of a major version + minor version + number of builds for the release. These are the actual software version numbers, as opposed to the values used for marketing.

If you go to Xcode menu > About Xcode, you'll see the Version and Build numbers. If you hit the More Info... button you'll see a bunch of different versions. Since the More Info... button was removed in Xcode 5, this information is also available from the Software > Developer section of the System Information app, available by opening Apple menu > About This Mac > System Report....

For example, Xcode 4.2 (4C139). Marketing version 4.2 is Build major version 4, Build minor version C, and Build number 139. The next release (presumably 4.3) will likely be Build release 4D, and the Build number will start over at 0 and increment from there.

The iPhone Simulator Version/Build numbers are the same way, as are iPhones, Macs, etc.

  • 3.2: (7W367a)
  • 4.0: (8A400)
  • 4.1: (8B117)
  • 4.2: (8C134)
  • 4.3: (8H7)

Update: By request, here are the steps to create a script that runs each time you build your app in Xcode to read the Build number, increment it, and write it back to the app's {App}-Info.plist file. There are optional, additional steps if you want to write your version/build numbers to your Settings.bundle/Root*.plist file(s).

This is extended from the how-to article here.

In Xcode 4.2 - 5.0:

  1. Load your Xcode project.
  2. In the left hand pane, click on your project at the very top of the hierarchy. This will load the project settings editor.
  3. On the left-hand side of the center window pane, click on your app under the TARGETS heading. You will need to configure this setup for each project target.
  4. Select the Build Phases tab.
    • In Xcode 4, at the bottom right, click the Add Build Phase button and select Add Run Script.
    • In Xcode 5, select Editor menu > Add Build Phase > Add Run Script Build Phase.
  5. Drag-and-drop the new Run Script phase to move it to just before the Copy Bundle Resources phase (when the app-info.plist file will be bundled with your app).
  6. In the new Run Script phase, set Shell: /bin/bash.
  7. Copy and paste the following into the script area for integer build numbers:

    buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
    buildNumber=$(($buildNumber + 1))
    /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
    

    As @Bdebeez pointed out, the Apple Generic Versioning Tool (agvtool) is also available. If you prefer to use it instead, then there are a couple things to change first:

    • Select the Build Settings tab.
    • Under the Versioning section, set the Current Project Version to the initial build number you want to use, e.g., 1.
    • Back on the Build Phases tab, drag-and-drop your Run Script phase after the Copy Bundle Resources phase to avoid a race condition when trying to both build and update the source file that includes your build number.

    Note that with the agvtool method you may still periodically get failed/canceled builds with no errors. For this reason, I don't recommend using agvtool with this script.

    Nevertheless, in your Run Script phase, you can use the following script:

    "${DEVELOPER_BIN_DIR}/agvtool" next-version -all
    

    The next-version argument increments the build number (bump is also an alias for the same thing), and -all updates Info.plist with the new build number.

  8. And if you have a Settings bundle where you show the Version and Build, you can add the following to the end of the script to update the version and build. Note: Change the PreferenceSpecifiers values to match your settings. PreferenceSpecifiers:2 means look at the item at index 2 under the PreferenceSpecifiers array in your plist file, so for a 0-based index, that's the 3rd preference setting in the array.

    productVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$INFOPLIST_FILE")
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:2:DefaultValue $buildNumber" Settings.bundle/Root.plist
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $productVersion" Settings.bundle/Root.plist
    

    If you're using agvtool instead of reading the Info.plist directly, you can add the following to your script instead:

    buildNumber=$("${DEVELOPER_BIN_DIR}/agvtool" what-version -terse)
    productVersion=$("${DEVELOPER_BIN_DIR}/agvtool" what-marketing-version -terse1)
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:2:DefaultValue $buildNumber" Settings.bundle/Root.plist
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $productVersion" Settings.bundle/Root.plist
    
  9. And if you have a universal app for iPad & iPhone, then you can also set the settings for the iPhone file:

    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:2:DefaultValue $buildNumber" Settings.bundle/Root~iphone.plist    
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $productVersion" Settings.bundle/Root~iphone.plist
    
nekno
  • 18,739
  • 4
  • 39
  • 45
  • 18
    "In my projects, I have a script that automatically increases the build number every time I build" - can you share how do you do that? thanks for the details anwers and for the original question. – Zsolt Oct 13 '11 at 09:52
  • 2
    @Andrews - I updated my answer with the details on the build script. – nekno Nov 15 '11 at 06:05
  • 9
    To increment In Hex numbers you can use `buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE") dec=$((0x$buildNumber)) buildNumber=$(($dec + 1)) hex=$(printf "%X" $buildNumber) /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $hex" "$INFOPLIST_FILE"` – Alon Amir Jan 25 '12 at 12:34
  • I'd love to be able to give it a +10. Amazing! Not wanting to look a gift horse in the mouth, do you think there's any way of setting the Build number to the SVN version number every time you commit? :) – jowie Jun 14 '12 at 09:54
  • I'm using this to automatically update my build numbers and it's great. But I just submitted an app for validation and I get the error: "This bundle is invalid. The key CFBundleVersion in the Info.plist file must be a period-separated list of non-negative numbers", so e.g. A0228 won't work. Changed the script to output decimal, but it isn't as cool! – Nicolas Miari Jul 02 '12 at 12:07
  • 1
    Guess only Apple can use geeky-looking build numbers – Nicolas Miari Jul 02 '12 at 12:09
  • 8
    In short: HEX is not allowed in the AppStore. – Nicolas Miari Jul 02 '12 at 12:10
  • 1
    Make sure `CFBundleVersion` is monotonically increasing. This is checked by the App Store validation check. – Ortwin Gentz Mar 04 '13 at 09:53
  • 3
    (Xcode 5 users) You may need to change step 5 to read: "From the Menu Bar, select Editor -> Add Build Phase -> Add Run Script Build Phase" – Greg M. Krsak Sep 20 '13 at 15:30
  • Is there option to write the _CFBundleVersion_ to plist file which is created by Enterrprise or _Ad-Hoc Deployment_? – kubo Oct 23 '13 at 15:15
  • @kubo - Yes, enterprise and ad-hoc deployments have the version and build numbers in the `Info.plist`, same as when submitted to the App Store. Since `CFBundleShortVersionString` and `CFBundleVersion` are updated by the script at _build time_, and packaging your app for enterprise or ad-hoc deployment using the **Distribute...** button in the Xcode Organizer window happens later, the appropriate version and build numbers are included in the `Info.plist` used for all deployments. Is there another plist file to which you're referring? – nekno Oct 28 '13 at 07:38
  • Awesome answer, but I'm having one issue. "buildNumber=$(($buildNumber + 1))" produces an error for me. The current setting is "1.0" -- I'm using Xcode 5.1. Do most developers change CFBundleVersion to an integer value instead of Apple's default "1.0" formatted version string? – chrisp Apr 12 '14 at 23:46
  • @jacobsd, just replace the second line of the bash script with the following: `buildNumber=$(echo "$buildNumber + 1" | bc)` – Max Gasner Jun 22 '14 at 01:17
  • I wanted to use Apple's avgtool for versioning, but it was failing on archive. Adding "exit 0" to the end of the run script corrected the problem so that a non-error code is returned. For more information see https://developer.apple.com/library/ios/qa/qa1827/_index.html – Dalmazio Sep 14 '14 at 21:50
  • 2
    Tip: If you check the box for the script "Run script only when installing" the buildVersion is only incremented when you generate an archive. – Bron Davies Aug 20 '15 at 18:26
  • 2
    Updated link for the how-to post about creating the script: https://davedelong.com/blog/2015/02/08/incrementing-build-numbers-in-xcode/ – Jim Nov 26 '17 at 19:21
  • FYI, the link to the `avgtool` in this answer is broken. Perhaps replace with link to [Technical Q&A QA1827](https://developer.apple.com/library/archive/qa/qa1827/_index.html)? – Rob Nov 20 '19 at 19:44
72

(Just leaving this here for my own reference.) This will show version and build for the "version" and "build" fields you see in an Xcode target:

- (NSString*) version {
    NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    return [NSString stringWithFormat:@"%@ build %@", version, build];
}

In Swift

func version() -> String {
    let dictionary = NSBundle.mainBundle().infoDictionary!
    let version = dictionary["CFBundleShortVersionString"] as? String
    let build = dictionary["CFBundleVersion"] as? String
    return "\(version) build \(build)"
}
Dan Rosenstark
  • 64,546
  • 54
  • 267
  • 405
  • 2
    OT: You have a leak in your method - you `alloc`/`init` the string, which retains the string, but you're not releasing it. On an object you return from a method, you should generally use a convenience method so the string is autoreleased automatically, or call `autorelease`. Either: `return [NSString stringWithFormat:@"%@ build %@", version, build];` OR `return [[[NSString alloc] initWithFormat:@"%@ build %@", version, build] autorelease];` – nekno Sep 06 '11 at 19:33
  • 1
    Thanks @nekno, changed answer so it's ARC or non-ARC friendly. – Dan Rosenstark Sep 07 '11 at 14:01
  • 2
    It's probably better to use the constants where available (e.g. kCFBundleVersionKey), to avoid typos. Though, I couldn't find one for "CFBundleShortVersionString" :) – DannyA Nov 20 '12 at 14:25
  • You have a bug in the swift code - you're calling CFBundleShortVersionString twice – Yariv Nissim Feb 26 '15 at 23:16
  • Thanks @yar1vn, I fixed it and NO it's not backwards. – Dan Rosenstark Feb 27 '15 at 00:39
54

The Build number is an internal number that indicates the current state of the app. It differs from the Version number in that it's typically not user facing and doesn't denote any difference/features/upgrades like a version number typically would.

Think of it like this:

  • Build (CFBundleVersion): The number of the build. Usually you start this at 1 and increase by 1 with each build of the app. It quickly allows for comparisons of which build is more recent and it denotes the sense of progress of the codebase. These can be overwhelmingly valuable when working with QA and needing to be sure bugs are logged against the right builds.
  • Marketing Version (CFBundleShortVersionString): The user-facing number you are using to denote this version of your app. Usually this follows a Major.minor version scheme (e.g. MyAwesomeApp 1.2) to let users know which releases are smaller maintenance updates and which are big deal new features.

To use this effectively in your projects, Apple provides a great tool called agvtool. I highly recommend using this as it is MUCH more simple than scripting up plist changes. It allows you to easily set both the build number and the marketing version. It is particularly useful when scripting (for instance, easily updating the build number on each build or even querying what the current build number is). It can even do more exotic things like tag your SVN for you when you update the build number.

To use it:

  • Set your project in Xcode, under Versioning, to use "Apple Generic".
  • In terminal
    • agvtool new-version 1 (set the Build number to 1)
    • agvtool new-marketing-version 1.0 (set the Marketing version to 1.0)

See the man page of agvtool for a ton of good info

NSGod
  • 21,896
  • 3
  • 51
  • 63
Bdebeez
  • 3,434
  • 3
  • 28
  • 32
  • another article about `agvtool` [Easy iPhone Application Versioning with agvtool](http://www.blog.montgomerie.net/easy-iphone-application-versioning-with-agvtool) – Gon Oct 21 '13 at 14:09
25

The script to autoincrement the build number in the answer above didn't work for me if the build number is a floating point value, so I modified it a little:

#!/bin/bash    
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=`echo $buildNumber +1|bc`
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
ale84
  • 1,396
  • 11
  • 16
22

The marketing release number is for the customers, called version number. It starts with 1.0 and goes up for major updates to 2.0, 3.0, for minor updates to 1.1, 1.2 and for bug fixes to 1.0.1, 1.0.2 . This number is oriented about releases and new features.

The build number is mostly the internal number of builds that have been made until then. But some use other numbers like the branch number of the repository. This number should be unique to distinguish the different nearly the same builds.

As you can see, the build number is not necessary and it is up to you which build number you want to use. So if you update your Xcode to a major version, the build field is empty. The version field may not be empty!.


To get the build number as a NSString variable:

NSString * appBuildString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];

To get the version number as a NSString variable:

NSString * appVersionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];

If you want both in one NSString:

NSString * versionBuildString = [NSString stringWithFormat:@"Version: %@ (%@)", appVersionString, appBuildString];

This is tested with Xcode Version 4.6.3 (4H1503). The build number is often written in parenthesis / braces. The build number is in hexadecimal or decimal.

buildandversion


In Xcode you can auto-increment the build number as a decimal number by placing the following in the Run script build phase in the project settings

#!/bin/bash    
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"

For hexadecimal build number use this script

buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=$((0x$buildNumber)) 
buildNumber=$(($buildNumber + 1)) 
buildNumber=$(printf "%X" $buildNumber)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"

project_settings

Binarian
  • 11,710
  • 8
  • 50
  • 82
6

Thanks to @nekno and @ale84 for great answers.

However, I modified @ale84's script it little to increment build numbers for floating point.

the value of incl can be changed according to your floating format requirements. For eg: if incl = .01, output format would be ... 1.19, 1.20, 1.21 ...

buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
incl=.01
buildNumber=`echo $buildNumber + $incl|bc`
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
iHS
  • 4,936
  • 3
  • 29
  • 48
1

Another way is to set the version number in appDelegate didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
     NSString * ver = [self myVersion];
     NSLog(@"version: %@",ver);

     NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
     [userDefaults setObject:ver forKey:@"version"];
     return YES;
}

- (NSString *) myVersion {
    NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    return [NSString stringWithFormat:@"%@ build %@", version, build];
}
Nilanshu Jaiswal
  • 1,265
  • 3
  • 15
  • 31