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.