Skip to main content

iOS Version and Build Number Rules: CFBundleShortVersionString

CFBundleShortVersionString is the iOS version users see; CFBundleVersion, the build number, must grow with each upload. Both are dot-separated integers.

The iOS version number, CFBundleShortVersionString, is the 1.2.0 users see on the App Store. The build number, CFBundleVersion, tells apart each upload of the same version. The rules come down to three points. Both can only contain integers separated by periods. Every upload for the same version needs a higher build number than the last. Once a version is released, increase the version before your next upload. Day to day, you only change the MARKETING_VERSION and CURRENT_PROJECT_VERSION build settings. Apple’s rules below are as of October 2026.

What are the iOS version and build number?

Version Build number
Info.plist key CFBundleShortVersionString CFBundleVersion
Build setting MARKETING_VERSION CURRENT_PROJECT_VERSION
Field on Xcode’s General page Version Build
Who sees it Users, on the App Store and in Settings Developers, in TestFlight and App Store Connect
Typical value 1.2.0 45, 20261019.1530
When to change it When you ship a new version Before every upload

Apple’s Upload builds help page says that App Store Connect uses the Bundle ID and version to file an upload under the right app and version record, and uses the build number to identify the build uniquely across the system. Each number has its own job, and you need both.

What format can the version and build number use?

Version, CFBundleShortVersionString

Apple’s CFBundleShortVersionString documentation specifies three period-separated integers, such as 10.14.1, containing only the digits 0–9 and periods. The three parts are the major version, minor version and patch number.

In practice, two-part values like 1.0 or 2.1 also upload. The simplest approach is to always write all three parts, such as 1.0.0. That matches the documented format exactly and leaves room for a patch number later.

Build number, CFBundleVersion

Apple’s CFBundleVersion documentation says the build number is one to three period-separated integers, containing only digits and periods. Missing parts are treated as 0, so 10 equals 10.0.0 and 10.5 equals 10.5.0. If you write more than three parts, the system ignores the extra ones.

The description of the Current Project Version build setting is stricter. It asks for an integer or a decimal number, such as 57 or 365.8. One or two parts is the safest choice for a build number.

Valid and invalid examples

Apple’s archived technical note TN2420 adds two rules. Both numbers must start and end with a digit, and neither can be longer than 18 characters.

Value As a version As a build number Why
1.2.0 Yes Yes Three integers
45 Not recommended Yes Write all three parts for the version
20261019.1530 Not recommended Yes Two integers, 13 characters
1.2.0.1 No Not recommended More than three parts
1.2.0-beta No No Contains letters and a hyphen
v1.2 No No Starts with a letter
1.2. No No Ends with a period
2026-10-19 No No Contains hyphens

An invalid version format fails the upload with ITMS-90060. Put test labels such as beta or rc in the branch name or in the TestFlight test notes, not in either number.

How they’re compared

Numbers are compared part by part as integers, not as strings. 1.10.0 is higher than 1.9.0, 46 is higher than 45.2, and 1.2.10 is higher than 1.2.9.

How does CFBundleShortVersionString relate to build settings?

Projects created in Xcode 13 and later turn on Generate Info.plist File (GENERATE_INFOPLIST_FILE) by default. Apple’s build settings reference says that with this setting on, the build writes MARKETING_VERSION as CFBundleShortVersionString and CURRENT_PROJECT_VERSION as CFBundleVersion.

In older projects with their own Info.plist file, check that both keys reference the build settings instead of hard-coded numbers.

1<key>CFBundleShortVersionString</key>
2<string>$(MARKETING_VERSION)</string>
3<key>CFBundleVersion</key>
4<string>$(CURRENT_PROJECT_VERSION)</string>

If the numbers are hard-coded, changing the build settings has no effect and the upload still carries the old numbers. This is the most common reason the version doesn’t change after you edit it.

Where frameworks keep the version

Framework Version comes from Build number comes from
Flutter The part before + in version: 1.2.0+45 in pubspec.yaml The 45 after +
Expo version in app.json ios.buildNumber
React Native (bare) MARKETING_VERSION in the ios/ project CURRENT_PROJECT_VERSION
Native Swift / Objective-C MARKETING_VERSION CURRENT_PROJECT_VERSION

A Flutter project’s Info.plist references $(FLUTTER_BUILD_NAME) and $(FLUTTER_BUILD_NUMBER), so change pubspec.yaml. Changing the two build settings in the Xcode project has no effect.

Why can’t a build number repeat within a version?

Apple calls all the builds uploaded for one version that version’s release train. Here are the rules from TN2420 and the Xcode help article Set the version number and build string.

Rule Details Error when you break it
No duplicate build numbers within a version Even a failed or deleted build’s number can’t be reused ITMS-90189
Build numbers increase within a version A new build number must be higher than every one used in this version The upload is rejected with a message that the build number must be higher
Versions can’t be reused and must increase A new version must be higher than the approved, released version ITMS-90062
The train closes once the version is released A released version accepts no new builds, and raising only the build number doesn’t help ITMS-90186
Extensions must match the main app on both numbers Every extension, such as a widget or notification extension, must match the main app A version mismatch warning at upload

What to do in common situations.

  • Resubmitting after a rejection. The version isn’t released and the train is still open, so increase only the build number and upload again.
  • Uploading another build after finding a problem in TestFlight. Same as above, increase only the build number.
  • Fixing a bug after 1.2.0 is released. Change the version to 1.2.1 and keep increasing the build number.
  • Upload rejected with ITMS-90189. Increase only the build number. For 90062 or 90186, increase the version.

TN2420 says iOS build numbers can repeat across versions, so 1.2.0 and 1.3.0 can both start at 1. Still, a build number that keeps increasing globally is simpler and avoids confusion when you read crash logs, so it’s the better choice. For detailed fixes for each error code, see App Store upload ITMS errors.

Why extension versions get out of sync

Extension targets have their own build settings. If the main app target defines its own MARKETING_VERSION and the extension isn’t updated with it, the two drift apart. The most reliable setup defines both settings only at the project level (PROJECT), with no target defining them itself, so every target inherits the project value. Extensions with their own Info.plist file must also reference $(MARKETING_VERSION) and $(CURRENT_PROJECT_VERSION).

Which build number scheme should you use?

Manage the version by hand along semantic versioning lines. Increase the major version for big changes, the minor version for new features and the patch number for bug fixes. Pick a build number scheme that suits your team.

Scheme Example build number Good for Watch out for
Manual increment 1, 2, 3 Solo developers, manual uploads Add one before every upload, or you get 90189
Date and time 20261019.1530 Several machines or pipelines uploading to the same app Use UTC. Two builds in the same minute collide
CI run number 128 or 128.2 A single pipeline that produces builds Reruns keep the same number, so add the attempt number as a second part
CI run number plus offset 1128 Switching to CI after uploading high numbers by hand The offset must be larger than the highest build number you’ve used

Avoid encoding the version into the build number, such as 120045 for build 45 of 1.2.0. Whenever the version changes you have to recalculate, and the number can end up going down.

For generating build numbers in CI and uploading automatically with an API key, see Auto-increment build numbers and upload .ipa files with an App Store Connect API key.

How do you change the iOS version in KXBuild?

When you use KXBuild on Windows or Linux, there are three ways to change them.

Permanently. Run KXBuild: Project Settings… from the Command Palette to open the Project Settings Editor. Select the app under TARGETS. On the General page, Marketing Version is the version and Current Project Version is the build number. Press Ctrl+S to save. To have extensions inherit them, switch to the Build Settings page, select PROJECT in the left column and change them there, then use the Levels view to confirm that no target defines either setting itself.

For one build. Add -D to the build command. It leaves the project file unchanged, which suits CI. See Override build settings.

1kxbuild build .\MyApp.xcodeproj -c Release --export app-store -D MARKETING_VERSION=1.2.1 -D CURRENT_PROJECT_VERSION=46

From a script that edits the project file. Without -t, project add-setting changes the project-level setting. See the kxbuild command reference.

1kxbuild project add-setting -k MARKETING_VERSION -v 1.2.1 -P .\MyApp.xcodeproj

If you use your own Info.plist file, open it in the Property List Editor and check that the iOS version and build number keys are $(MARKETING_VERSION) and $(CURRENT_PROJECT_VERSION). For the full pre-release checklist, see Release and App Store submission.

FAQ

Can an iOS version number be just 1.0?

It uploads, but Apple’s documentation specifies three integers, so write 1.0.0. The version can only contain digits and periods, with no letters such as beta or v.

Do I need to change the version number when resubmitting after a rejection?

No. The version isn’t released yet, so increase only the build number, upload again, then select the new build for this version in App Store Connect and submit.

Can the build number be the same as the version number?

Yes, a version of 1.2.0 with a build number of 1.2.0 is valid. But the next upload for the same version would need something like 1.2.0.1, which has more than three parts, so use an independently increasing integer for the build number.

Why is the old version still showing after I changed MARKETING_VERSION?

Usually Info.plist has a hard-coded number instead of a reference to the build setting. Change CFBundleShortVersionString to $(MARKETING_VERSION), and check the extensions’ Info.plist files too. In a Flutter project, change version in pubspec.yaml.

What should I watch for when using a timestamp as the build number?

Write it as two integers, date then time, such as 20261019.1530, keep it to 18 characters or fewer, and leave out hyphens and colons. When several machines build, use UTC on all of them.