CFBundleVersion: The version of the build that identifies an iteration of the bundle. This key is a machine-readable string composed of one to three period-separated integers, such as 10.14.1. The string can only contain numeric characters (0-9) and periods.
https://developer.apple.com/documentation/bundleresources/in...
CFBundleShortVersionString: The release or version number of the bundle. This key is a user-visible string for the version of the bundle. The required format is three period-separated integers, such as 10.14.1. The string can only contain numeric characters (0-9) and periods.
https://developer.apple.com/documentation/bundleresources/in...
Android similarly has versionName and versionCode.
The problem is that no matter what, someone somewhere is going to parse something that was never intended for non-human consumption. Or code ends up making assumptions that it shouldn't about a machine-readable version.
I have code which parses CFBundleVersion. It assumes the version follows Apple's docs. But guess what? CFBundleVersion is just a string and nothing enforces Apple's guidance so I was surprised (I shouldn't have been) to have to deal with an internally distributed iOS app that had the word "debug" tacked on to the end of its CFBundleVersion.