jherran
jherran

Reputation: 3367

Use same CFBundleVersion and CFBundleShortVersionString in all targets

I received the following email from Apple when I submit an app update:

We have discovered one or more issues with your recent delivery for "Project". Your delivery was successful, but you may wish to correct the following issues in your next delivery:

CFBundleVersion Mismatch - The CFBundleVersion value '1' of extension 'Project.app/PlugIns/ProjectTodayExtension.appex' does not match the CFBundleVersion value '985' of its containing iOS application 'Project.app'.

CFBundleShortVersionString Mismatch - The CFBundleShortVersionString value '1.0' of extension 'Project.app/PlugIns/ProjectTodayExtension.appex' does not match the CFBundleShortVersionString value '2.1.6' of its containing iOS application 'Project.app'.

After you’ve corrected the issues, you can use Xcode or Application Loader to upload a new binary to iTunes Connect.

Is there any way of use the same CFBundleVersion and CFBundleShortVersionString in all targets to prevent this?

Upvotes: 15

Views: 9589

Answers (8)

horistar
horistar

Reputation: 21

I utilized the excellent answer from stk, but there was a snag that I wanted to mention so others are aware:

Setting the user-defined constants for the primary target (main app executable) resulted in errors when uploading the app to the store. At least in Xcode 13.0, Xcode crashed when it was trying to create a URL parameter to verify the version number with the server. Directly setting the version numbers for the main target fixed the issue. All the other "sub-targets" work fine using the defined constants.

Upvotes: 0

Crt Gregoric
Crt Gregoric

Reputation: 402

Xcode 12

I was able to achieve keeping the target extension version and build strings in sync with the main app with the following:

  1. Add a Run Script Phase above Compile Sources
  2. Add the following to the content of the newly created Run Script:
WIDGET_EXTENSION="${SRCROOT}/MyWidget/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${MARKETING_VERSION}" "$WIDGET_EXTENSION"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${CURRENT_PROJECT_VERSION}" "$WIDGET_EXTENSION"

Upvotes: 5

Nike Kov
Nike Kov

Reputation: 13726

Xcode 11+

1) Go to project settings, choose PROJECT (above targets), go to Build Settings
2) Press Add User-Defined Setting and add APP_BUILD and APP_VERSION
3) In your targets go to Info and replace Bundle version to $(APP_BUILD) and Bundle version string to $(APP_VERSION) in all targets and extensions where you need to sync.
4) Woila! You should change bundle versions only in one place - in Build Settings of your PROJECT.

Upvotes: 2

markgo2k
markgo2k

Reputation: 295

There is a really sweet version management system that Twitch has shared.

Described in this blog post, it is somewhat similar to stk's accepted answer but cleaner and also supports the following:

  • Ties the build number directly (and reversibly) to the git commit before the build. Go back easily to the exact version built for use with crash report.

  • Handles version generation through a target dependency, which is easier to share across multiple targets.

  • Uses C Preprocessor on Info.plist functionality built into Xcode build settings to allow the version numbers to be substituted on the fly, with no modification of the Info.plist file.

It is a little more complex to implement, but it's the best solution I've found, particularly if you have extensions or other targets whose versions must be kept in sync.

Installation Notes: Note the blog does a great job of describing the four shell files, but doesn't really give installation or customization instructions. Here's what I did:

  1. Create a Versions subdirectory at the top level of your project (where the .xcodeproj lives).

  2. Download the four files indicated from the gist link at bottom left of the code samples. Move the four files into your Versions directory.

  3. Using terminal, cd to your Versions directory, then execute the cmd: chmod +x * to make the shell files executable

  4. Now follow the directions in the blog from the start to create your dependency target.

  5. You probably should customize the scripts a bit. I altered the naming and refactored to move the 4 tools to a separate tools directory that I share across projects. YMMV.

Upvotes: 2

Ivar Rand
Ivar Rand

Reputation: 1

When trying to validate my archive I got an error message CFBundleShortVersionString missing. To fix the problem I went into Info.plist in the xml code and added CFBundleShortVersionString new version number This produced in plist format Bundle versions string, short This solve my problem

Upvotes: -1

Oren
Oren

Reputation: 5103

The scheme actions aren't in source control so it's better to add a build phase into your app's target. Syncing the versions across all targets can be solved with a simple script that can be modified for every target you want synced:

  1. Add "New Run Script Phase" in "Build Phases" for your app's target

enter image description here

  1. Rename the script to something like "Sync Versions" and drag it above "Compile Sources" (NOTE: Xcode has a bug that may prevent the drag-drop to work. If so, you'll need to manually edit the .pbxproj file so the build phase goes in the right spot

  2. Paste the following script into the shell:

    INFOPLIST_MYAPP="${SRCROOT}/MyApp/MyApp-Info.plist"
    myAppVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$INFOPLIST_MYAPP")
    myAppBuild=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_MYAPP")
    
    INFOPLIST_SHAREEXT="${SRCROOT}/ShareExtension/Info.plist"
    /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $myAppVersion" "$INFOPLIST_SHAREEXT"
    /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $myAppBuild" "$INFOPLIST_SHAREEXT"
    

enter image description here

  1. Build your project as you normally and your share extension's version & build will stay in sync with your main target.

Upvotes: 16

stk
stk

Reputation: 6461

My solution is:

For CFBundleShortVersionString:

  • Add a user-defined constant in your project settings

Add a user-defined constant in your **project** settings

  • Name it $(CF_BUNDLE_SHORT_VERSION_STRING) and set it to your desired value

enter image description here

  • Set your version in your targets to $(CF_BUNDLE_SHORT_VERSION_STRING)

enter image description here

  • Repeat for all targets. Done!

CFBundleVersion: you could do the same for CFBundleVersion, but somehow I wanted this value to be computed from my GIT repo commit count. I´ve done it like this:

  • Add a Pre-action to your main target. You access the shown dialog via Product > Scheme > Edit Scheme

enter image description here

  • Add a Post-action to your main target.

enter image description here

  • Add a new Command Line Tool target named BundleVersionUpdate and one named BundleVersionRevert

enter image description here

  • Navigate to your new BundleVersionUpdate target and add a new Run Script Build Phase

enter image description here

  • Paste the following
\#!/bin/sh

INFOPLIST="${SRCROOT}/MyApp/MyApp-Info.plist"
INFOPLIST_WKAPP="${SRCROOT}/MyApp-WKApp/Info.plist"
INFOPLIST_WKEXT="${SRCROOT}/MyApp-WKExt/Info.plist"

PLISTCMD="Set :CFBundleVersion $(git rev-list --all|wc -l)"

echo -n "$INFOPLIST" 
| xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD"

echo -n "$INFOPLIST_WKAPP" 
| xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD"

echo -n "$INFOPLIST_WKEXT" 
| xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD"
  • Navigate to your new BundleVersionRevert target and add a new Run Script Build Phase and paste this:
\#!/bin/sh

INFOPLIST="${SRCROOT}/MyApp/MyApp-Info.plist"
INFOPLIST_WKAPP="${SRCROOT}/MyApp-WKApp/Info.plist"
INFOPLIST_WKEXT="${SRCROOT}/MyApp-WKExt/Info.plist"

PLISTCMD="Set :CFBundleVersion SCRIPTED"

echo -n "$INFOPLIST" 
| xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD"

echo -n "$INFOPLIST_WKAPP" 
| xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD"

echo -n "$INFOPLIST_WKEXT" 
| xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD"
  • Enjoy!

Upvotes: 17

The version of your ProjectTodayExtension.appex have to be the same as your app. example:

Target> General:

Version: 1.0 <- change here Biuld: 1.0

If the version of your app to get on itunes connect is 2.3, then you must change the version of your TodayExtension to the same version 2.3.

Upvotes: 1

Related Questions