Two numbers describe every iOS app you upload, and mixing them up causes the most common rejection of all: "the bundle version must be higher than the previously uploaded version." One number is for your users, the other is for Apple's systems. The version is the public release number people see on the App Store; the build is the internal counter that has to climb with every upload. Here is exactly what each one is, the Info.plist keys behind them, and how they work together.
Short answer
On iOS, the version number is CFBundleShortVersionString, the user-facing release number like 1.2.0 that appears on the App Store and in Settings, and the build number is CFBundleVersion, an internal counter that identifies a specific build of that version. The key rule is that each binary you upload to App Store Connect for the same version must have a higher build number than the last, while the version number changes only when you ship a new release to users. So you might have version 1.2.0 with builds 1, 2, and 3 in TestFlight, then submit version 1.2.0 build 3 to the App Store.
What you should know
- Version is for users:
CFBundleShortVersionStringis the public release number, like 1.2.0. - Build is for uploads:
CFBundleVersionidentifies a specific binary of that version. - Build must increase: every upload for the same version needs a higher build number.
- Version changes per release: you bump it when shipping a new version to users.
- They are separate keys: both live in Info.plist and serve different purposes.
What is the version number?
The version number is the public, marketing-facing release number, stored in CFBundleShortVersionString. It is what users see on your App Store listing and in the iOS Settings app, and it typically follows semantic versioning, like 1.0.0 for launch, 1.1.0 for a feature update, and 1.0.1 for a bug fix. You change it when you release a new version of the app to the public, and the App Store treats a new version string as a new release that goes through App Review. So the version number communicates to users which release they have, and it should move in a way that makes sense as a product history, not on every internal build.
What is the build number?
The build number is the internal identifier for a specific build, stored in CFBundleVersion. It exists so Apple's systems and TestFlight can tell one upload of a version apart from another, and its defining rule is that it must increase with each upload for the same version string. It does not need to be user-friendly, so many teams use a simple incrementing integer or a date-based scheme. Users generally do not see the build number on the store, though it appears in TestFlight and crash reports. The build number is the value that causes the "bundle version must be higher" rejection when you forget to bump it before re-uploading.
How they work together
The version groups releases; the build counts the uploads within a release. The table shows a typical sequence.
| Stage | Version (CFBundleShortVersionString) | Build (CFBundleVersion) |
|---|---|---|
| First TestFlight build | 1.2.0 | 1 |
| Fix, re-upload to TestFlight | 1.2.0 | 2 |
| Final build submitted to App Store | 1.2.0 | 3 |
| Next public release | 1.3.0 | 4 |
The pattern is that the build number keeps climbing across everything you upload, while the version number only advances when you ship a new release to users. For a fix during testing, increment the build and keep the version the same, which also keeps you on the same Beta App Review train rather than starting a new one.
What to watch out for
The first trap is re-uploading without bumping the build number, which produces the "bundle version must be higher than the previously uploaded version" error; bump CFBundleVersion and re-archive. The second is bumping the version string for a small in-testing fix, which sends a new version's first build back through Beta App Review when a build bump would have stayed on the fast path. The third is losing track of which build you actually tested, since the build number is how you map a crash report or a security check back to a specific binary. A pre-submission scan such as PTKD.com (https://ptkd.com) reads the exact compiled IPA against OWASP MASVS, so noting the build number you scanned ties the result to the binary you submit, rather than to an earlier build that has since changed.
What to take away
- The version number,
CFBundleShortVersionString, is the user-facing release like 1.2.0 shown on the App Store; the build number,CFBundleVersion, identifies a specific binary of that version. - Every upload for the same version must have a higher build number, while the version string changes only when you ship a new release.
- For a fix during testing, increment the build and keep the version the same to avoid restarting Beta App Review.
- Track the build number you test and scan, so a pre-submission scan such as PTKD.com maps to the exact binary you submit.




