This is my first iOS app submission and I don't want my app rejected.
This is from the Apple Docs:
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.
The value for this key differs from the value for “CFBundleVersion,” which identifies an iteration (released or unreleased) of the app. This key can be localized by including it in your InfoPlist.strings files.
But it seems a bit strange. My interpretation for this is to put both values the same, i.e.:
CFBundleVersion: 1.0.0
CFBundleShortVersionString: 1.0.0
Can someone confirm 100% that is what I am supposed to put?
CFBundleShortVersionString gives you the version of your app. It's typically incremented each time you publish your app to the App Store. This is the version that is visible on the "Version" section for the App Store page of your application.
CFBundleVersion gives you the build number which is used for development and testing, namely "technical" purposes. The end user is rarely interested in the build number but during the development you may need to know what's being developed and fixed on each build. This is typically incremented on each iteration of internal release. And you can use continuous integration tools like Jenkins to auto-increment the build number on each build.
https://i.stack.imgur.com/HNrrs.jpg
The two numbers do not depend on each other but it is a good idea to keep them parallel to avoid confusion. Keep in mind that once your app has passed the App Store review you need to increment the build number like Phil and likeTheSky have stated, regardless of whether you publish it or not.
Use case: Let's say, you have a well-tested build, ready for submission. It's version number is 1.0.0 and build number is 1.0.0.32. Once you submit your app, you need to update the version as 1.0.1 and build number as 1.0.1.0.
Think of it this way: The "short version" (CFBundleShortVersionString
) is the public version number. The "version" (CFBundleVersion
) is more of an internal version number that could change far more frequently than the public "short version". Personally I use the same for both but many people update the "version" on every build. Either way you typically update the "short version" when you release to Apple. How often you update the "version" is up to you and your needs.
The answer by rmaddy is correct. I'll add two more thoughts.
Third Version Number
Be aware of the third version number, specified on the iTunesConnect web site as part of your app's definition. If that number is different than the two in Xcode, Apple gives you a warning. You can ignore the warning, as it is not a show-stopper (not an "error").
Date-Time as version
Also, you need not use three numbers with punctuation. That may may sense for some apps, where traditionally changes in the first number indicated some kind of dramatic change usually affecting compatibility.
For other apps you might want to use simply a date-time value in ISO 8601 standard format style (YYYYMMDDHHMM). For example, 201606070620
. That order of year-month-date-hour-minute renders an ever-increasing number, always the same length due to padding zero, that when sorted alphabetically is also chronological.
I have successfully used this style of version numbers on a shipping iOS app working in iOS 7, 8, & 9.
You can even automate the generation of this value. In your project’s Target
> Build Phases
> Run Script
panel:
Specify in the Shell field: /bin/sh Paste the following 5 line script seen below. (optional) Check the Show environment variables in build log checkbox. Uncheck the Run script only when installing checkbox.
Every time you do a build the current date-time in UTC time zone is captured. The -u
flag in the script makes use of UTC rather than your current default time zone. Generally best for programmers and sysadmins to be using and thinking in UTC rather than local time zones.
#!/bin/bash
buildNumber=$(date -u "+%Y%m%d%H%M")
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $buildNumber" "$INFOPLIST_FILE" # Version number
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE" # Build number
echo "DateTime for app version number: $buildNumber"
Or do a hybrid, with a conventional 1.2.3
for the Version number and a date-time as the Build number. To do the hybrid, simply comment-out the CFBundleShortVersionString
line with a #
in front.
The scheme most sensible to me is to use the version number (ie. CFBundleShortVersionString
) for the actual version number, and then use the build number (ie. CFBundleVersion
) to represent the submission to the App Store. So unless there are any problems and hence re-submits, this number is always 1. For a new release, I reset to 1 if the previous had issues in TestFlight testing or in review.
Build numbers provide a way to name each of the submissions you provide for a particular release. As described in the definitions above, the collection of all of the builds that you provide for a particular version of your app is called that version's 'release train'. For iOS apps, build numbers must be unique within each release train, but they do not need to be unique across different release trains [my emphasis]. That is to say, for iOS Apps you can use the same build numbers again in different release trains if you want to.
From Technical Note TN2420: Version Numbers and Build Numbers.
I use CFBundleVersion to indicate internal build for CFBundleShortVersionString. I use test flight to submit builds for my testers so the difference between them has been extremely useful.
Apple documents says CFBundleVersion "should be a string comprised of 3 non-negative, period-separated integers" But actually it can be MORE THAN 3 parts(as the above answer shows). I use that to indicate my development build, say my CFBundleShortVersionString is 1.0.0, I can use 1.0.0.11 for CFBundleVersion to indicate that is my 11th build for release 1.0.0
Each CFBundleVersion submitted to app store should be bigger than before or you will get ERROR ITMS-90478: "Invalid Version. The build with the version “xxx” can’t be imported because a later version has been closed for new build submissions. Choose a different version number."
CFBundleShortVersionString can only have 3 parts or you will get ERROR ITMS-90060:The value for key CFBundleShortVersionString 'xxx' in the Info.plist file must be a period-separated list of at most three non-negative integers."
The 3rd number that Basil Bourque mentioned, i.e. the version number shows on iTunesConnect is where things may get complicated.
I use a different iTunesConnect number than CFBundleShortVersionString because when I first submitted my app to app store we already have many rounds of internal releases. So I used 1.0 for iTunesConnect number and 5.x for CFBundleShortVersionString. In the next release to app store I provided a function to check if there is a newer version in the app store and realized I had trouble now because I can only get iTunesConnect number (using http://itunes.apple.com/lookup?bundleId=
) so I need to do some calculation to before compare it with CFBundleShortVersionString number.
I tried to fix that by using iTunesConnect number as my CFBundleShortVersionString, but got the error, ERROR ITMS-90062: "This bundle is invalid. The value for key CFBundleShortVersionString [x.x.x] in the Info.plist file must contain a higher version than that of the previously approved version [x.x.x]."
So I will suggest always make them the same.
As-of now, the Apple documentation for CFBundleVersion
states [emphasis mine]:
The build version that identifies an iteration of the bundle. ... This key is a machine-readable string composed of one to three period-separated integers, such as 10.14.1. The string can only contain numeric characters (0-9) and periods. ... You can include more integers but the system ignores them.
For CFBundleShortVersionString
[emphasis mine]:
The release or version number of the bundle. ... This key is a user-visible string for the version of the bundle. The required format is three period-separated integers, such as 10.14.1. The string can only contain numeric characters (0-9) and periods.
I'd suggest just automatically incrementing CFBundleVersion
for each build (or every release to TestFlight) and resetting it to 0 whenever you change CFBundleShortVersionString
.
You should explicitly plan, or devise a consistent means, to update the user visible version in CFBundleShortVersionString
.
Something I've never seen discussed anywhere is what is the maximum number for each field in a CFBundleVersion?
By setting CFBundleVersion in a app to 1.1.1 and looking at the hexadecimal vaue for the version in "lsregister -dump", I determined that the maximum value for the first field is (2^22)-1 or 4194303, and the maximum values for the second and third fields are (2^21)-1 or 2097151.
The 3 fields add up to 64 bits.
This has implications for those of us who use CFBundleVersion based on date and time.
I was setting the first field to YYYYMMDD. This is always greater than the max allowed versions and it was leading to unpredictable results, to say the least, when Launch Services was deciding which version of an app to run when you had multiple versions installed and were using something like 'open -a Appname' from the command line.
Please spread this widely. I am sure a lot of people are coming unstuck with this.
Success story sharing
201606070620
in a shipping iOS app.