I have released quite a few apps already, mostly at work but also personally. When releasing, I was facing issues with signing, Xcode configurations, CI/CD solutions or the AppStore Review. I assumed I have seen quite a lot of issues. Recently I was facing an interesting case I never encountered before, and I was very surprised something like this even exists.
I use an SDK, which internally reads the CFBundleVersionShortString
and CFBundleVersion
values from its bundled Info.plist
. I noticed strange behaviour on TestFlight builds. The values got modified and were overridden with the values from the iOS target of my app in a release build. This caused some issues since the SDK was relying on these values. I was a little bit baffled at first that there seemed to be some setting which was altering my files without me implementing something like that. But then was able to find out why this was happening. In the following, I want to show you what caused this issue and how it could be fixed.
Manage Version and build number
I am only using CI/CD solutions to export apps, so either fastlane
, Xcode Cloud
or Bitrise
and not Xcode directly. So I searched a little bit online but could not find something that matched my issue. Then I went ahead and tried to export and upload my app the good old manual way with Xcode directly. And then I found it! When exporting and uploading your app via Xcode to AppStore Connect you come across the following screen.
And here it says:
Manage version and build number
This will change the version and build number of all content in your app to ExecutableTarget.CFBundleVersionShortString (ExecutableTarget.CFBundleVersion)
It was exactly the issue I was facing. Now knowing the correct "term" I was able to find out more. This is an option that was introduced with Xcode 13.0 and per default is true. It is also mentioned in the official Apple Documentation for Distribution. As soon as the checkbox is unchecked all of Info.plist
files stayed the same and nothing was overridden any longer.
Disable manage version and build number programmatically
To disable this behaviour programmatically, e.g. for the usage in a CI/CD environment you need to add the manageAppVersionAndBuildNumber
key in your ExportOptions.plist
and set its value to false
.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>manageAppVersionAndBuildNumber</key>
<false/>
</dict>
With that in place, you opt out of the default behaviour and the Info.plist
files of all of your frameworks are untouched.
❗ Be aware that if you disable this option and you implement a watchOS companion app or include app extensions in your executable, you have to manually make sure that CFBundleVersionShortString
and CFBundleVersion
do match the main executable.
Why this option exists and how to handle it
After all of the issues had been resolved I was asking myself, why Apple introduced this option and made it true by default. I couldn't find an official answer to this, so I can only guess. I think the primary use case for this option is that it also causes the Info.plist
files of the extension to be aligned with the main executable target, which on iOS is a requirement e.g. for a Widget extension. So it seems to be rather a safeguard for developers in case they forgot to align these values.
While I do like good defaults, in that case, it caused more issues for me than it helped. As soon as I had some "official name" for the feature I found a lot of entries of other developers facing the same issue. There were a lot of cases where 3rd party SDKs relied on reading the CFBundleVersionShortString
and CFBundleVersion
values from its bundled Info.plist
and then behaved weirdly because the values did not match their expectations.
So I think we need to be careful with this option and consider the following points:
- As an SDK or library developer it's not safe to rely on
CFBundleVersionShortString
andCFBundleVersion
values set in theInfo.plist
file. They can be intentionally or accidentally overridden by the application the artefacts are included in. So it's safer to use custom keys for the version and build number if the code relies on those. Or if there is a reason to not use custom keys, communicate to consumers that these values must not be altered. - As an app developer we need to ensure that the option is set in a way that does not break embedded internal or external code.
Conclusion
I hope I was able to highlight this rather hidden option within the AppStore/Xcode ecosystem and what issues can arise from using it incorrectly. As soon as you know what's going on, the issue can get fixed with small changes, but you need to know where to look at.
If you find any mistake or would like to reach out to me please feel free to do so.
See you next time! 👋