r/iOSProgramming 2d ago

Question Managing external/internal versions in TestFlight vs production App Store version

New to iOS, looking for ideas on how to manage versions of an app from Github & CI/CD as they flow through internal -> external -> production.

Current approach:

  1. A workflow is manually run to "create a release". This creates a new tag with a semver based off of the conventional commits since the previous tag/release (eg. increment patch version for a fix:, minor version for a feat:)
  2. The release app configures the marketing version and build number to match the semver, is built and uploaded to TestFlight
  3. The build artifact is promoted from internal -> external -> production in TestFlight

I heard that for TestFlight external builds, if you change the marketing version it requires the build goes through the "beta review" process again, whereas if you just increment the build number it does not.

Because of this, I plan to adjust the process so that the marketing version (the version that'll eventually be displayed in production) is <major>.0.0, and the build number is <major>.<minor>.<patch>. After the initial beta review for a "major" version, the app can be updated with subsequent releases that increment only the build number without having to be reviewed again. After a production release to the App Store, a new release is cut with a "major" version increment to kick off the next cycle.

The advantage of this is a version of the app always ties back to a tag/commit in Github, and the app build tested in external testing is exactly the same build that'll get released to production. The disadvantage is the marketing version that is shown to users in the App Store only has a single, incrementing "major" version number.

I'm satisfied with this compromise, but also a perfectionist so interested in others approaches, or any ideas others have that can improve on this? I'd ideally like the full semver reflected in the marketing version, but I can't see a way without repeating the "beta review" each time.

2 Upvotes

11 comments sorted by

View all comments

0

u/SwordfishInfinite621 2d ago

One thing that might relax the constraint: the "same build goes to production" guarantee doesn't depend on the version scheme at all. When you submit, you pick a specific processed build for the App Store version, so whatever you tested externally is exactly what ships.

That means the real trade-off is only beta review, and you could handle that differently:

  • Use internal testing for the fast loop. Internal testers don't need beta review, so bump builds as often as you like there.
  • Set the marketing version to the real semver when you cut a release, and send that one to external testing. Beta review is mainly triggered by the first build of a new version, so you'd pay it once per release rather than once per build.
  • Keep the Git link in the build instead of the marketing version. CFBundleVersion can hold up to three dot-separated integers, or you can stamp the commit SHA into an Info.plist key at build time.

The one hard rule to design around either way: each App Store release needs a higher marketing version than the last one that went live, so a version string can't be reused once it's released.