Get Started with Appcircle
Save time, reduce costs, and increase developer productivity now.
Get informed about news, new releases, and mobile DevOps.
Learn iOS app versioning rules for version numbers and build numbers, App Store constraints, and best practices with Appcircle automation.
iOS apps use two numbers to track releases: the version number (also called the marketing version) and the build number (also called the internal version code). These values must be managed carefully because the App Store requires them to follow strict rules for publishing updates.
• The version number (CFBundleShortVersionString) is what users see in the store (for example, 2.3.1). It follows Semantic Versioning principles to show whether a release is a bug fix, a new feature, or a major update.
• The build number (CFBundleVersion) is an integer that must always increase with each submission, ensuring the stores recognize it as a newer build.
Example: An app is released with version 1.2.0 (build 15). The team fixes a minor bug and publishes version 1.2.1 (build 16). Later, they add a new feature and release 1.3.0 (build 17). iOS enforce that the build number increases each time, while the version number communicates the type of change to end users.
In iOS, the version number (stored in CFBundleShortVersionString inside Info.plist) is the marketing version that users see in the App Store. Unlike the build number, it does not need to increase with every submission, but it should follow a clear and consistent strategy such as Semantic Versioning in the form of MAJOR.MINOR.PATCH.
In Appcircle, the version number can be defined in several ways. It can be set dynamically using an environment variable during the build pipeline, pulled from the current published version on the App Store to align new releases, or managed manually in Xcode by editing the value in Info.plist. To give teams more control, Appcircle also allows applying an offset and choosing an increment strategy. Depending on the release type, teams may increment only the patch version for bug fixes, the minor version for backward-compatible new features, or the major version when introducing breaking changes.
Example: A team configures Appcircle to reference the current App Store version of 1.4.0 with the increment strategy set to minor. The pipeline automatically updates the version to 1.5.0 for the next submission. Another team configures Appcircle to use the version defined in Info.plist as the source. With the increment strategy set to patch, Appcircle automatically updates the version from 2.0.0 to 2.0.1, 2.0.2, and so on for each bug fix release. Both approaches ensure that version numbers remain consistent and meaningful while meeting App Store requirements.
In iOS, the build number (stored in CFBundleVersion inside Info.plist) uniquely identifies each release and must always increase for submissions to the App Store or TestFlight.
In Appcircle, the build number can come either from an environment variable in the pipeline or from the value defined in Xcode’s Info.plist. The final number is then incremented based on the configured offset, ensuring every build has a unique value.
Example: A team might configure their pipeline to use the BUILD_ID environment variable with Appcircle's Environment Variables feature. For example, when the build ID is 25 and the offset is 10, the resulting build number becomes 35. In another case, a developer may set CFBundleVersion to 42 in Info.plist, and with the same offset of 10, the submitted build number becomes 52. Both approaches ensure that build numbers remain unique and always increase across TestFlight and App Store submissions.
Apple enforces strict versioning requirements for all iOS, iPadOS, tvOS, and watchOS apps submitted to the App Store or TestFlight. These rules ensure that every build is uniquely identifiable and that version numbers remain consistent across releases.
Version Number (CFBundleShortVersionString):
• Must follow the Major.Minor.Patch format.
• Each component must be a non-negative integer.
Build Number (CFBundleVersion):
• Must be a non-negative integer
• Must always increase with every new submission to TestFlight or the App Store, even if the version number remains the same.
Common pitfalls that cause rejections:
• Reusing the same build number for multiple submissions.
• Skipping increments or rolling back build numbers.
• Using an invalid format for version numbers (for example, adding extra digits or leading zeros).
Example: A team uses Appcircle to automate their versioning strategy. By letting Appcircle manage both the version number and build number automatically, they ensure that every submission follows Apple's rules. This prevents common pitfalls like reusing build numbers or skipping increments, guaranteeing their apps won't be rejected from the App Store due to versioning errors.
Managing version numbers and build numbers correctly is critical to ensure smooth App Store submissions and clear communication with users. Following these best practices helps teams avoid rejections and maintain consistency across environments:
• Align with Semantic Versioning (SemVer): Use the MAJOR.MINOR.PATCH format to indicate whether the update is a major change, a new feature, or a bug fix.
• Automate build numbers: Configure CI/CD pipelines to increment the build number automatically with every new submission to stay compliant with App Store rules.
• Use offsets across environments: Apply offsets to keep versioning synchronized between development, staging, and production builds, which prevents conflicts when multiple pipelines run in parallel.
• Simplify public versions: Use whole numbers such as 1.0 or 2.0 for major public releases and decimals such as 1.1 or 1.2 for smaller updates that remain backward compatible.
• Increment build numbers consistently: Every App Store submission must have a higher build number than the previous one, even if the version number does not change.
Example: A developer team configures Appcircle to automate their iOS versioning strategy. Once set up, Appcircle automatically increments build numbers, applies offsets across environments, and enforces Semantic Versioning rules. This means developers can focus on building new features and improving the app, rather than worrying about versioning details or App Store rejections.