I would like to include the application version and internal revision, something like 1.0.1 (r1243), in my application\'s settings bundle.
The Root.plist file contai
My working Example based on @Ben Clayton answer and the comments of @Luis Ascorbe and @Vahid Amiri:
Note: This approach modifies the Settings.bundle/Root.plist file in working copy of repository
Add a settings bundle to your project root. Don't rename it
Open Settings.bundle/Root.plist as SourceCode
Replace the contents with:
<?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>PreferenceSpecifiers</key>
<array>
<dict>
<key>DefaultValue</key>
<string></string>
<key>Key</key>
<string>version_preference</string>
<key>Title</key>
<string>Version</string>
<key>Type</key>
<string>PSTitleValueSpecifier</string>
</dict>
</array>
<key>StringsTable</key>
<string>Root</string>
</dict>
</plist>
Add the following script to the Build, Pre-actions section of the project (target) scheme
version=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$PROJECT_DIR/$INFOPLIST_FILE")
build=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$PROJECT_DIR/$INFOPLIST_FILE")
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:0:DefaultValue $version ($build)" "${SRCROOT}/Settings.bundle/Root.plist"
Build and Run the current scheme
Based on @Quinn's answer, here the full process and working code I use to do this.
Replace the contents with:
<?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>PreferenceSpecifiers</key>
<array>
<dict>
<key>Title</key>
<string>About</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>DefaultValue</key>
<string>DummyVersion</string>
<key>Key</key>
<string>version_preference</string>
<key>Title</key>
<string>Version</string>
<key>Type</key>
<string>PSTitleValueSpecifier</string>
</dict>
</array>
<key>StringsTable</key>
<string>Root</string>
</dict>
</plist>
Create a Run Script build phase, move to be after the Copy Bundle Resources phase. Add this code:
cd "${BUILT_PRODUCTS_DIR}"
buildVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${INFOPLIST_PATH}" )
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $buildVersion" "${WRAPPER_NAME}/Settings.bundle/Root.plist"
Replace MyAppName with your actual app's name, and the 1 after PreferenceSpecifiers to be the index of your Version entry in the Settings. The above Root.plist example has it at index 1.
Using Ben Clayton's plist https://stackoverflow.com/a/12842530/338986
Add Run script with following snippet after Copy Bundle Resources.
version=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$PROJECT_DIR/$INFOPLIST_FILE")
build=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$PROJECT_DIR/$INFOPLIST_FILE")
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $version ($build)" "$CODESIGNING_FOLDER_PATH/Settings.bundle/Root.plist"
Appending CFBundleVersion in addition of CFBundleShortVersionString.
It emit version like this:

By writing to
$CODESIGNING_FOLDER_PATH/Settings.bundle/Root.plist
instead of the one in $SRCROOT have some benefits.
Settings.bundle in $SRCROOT. The path may vary. Testing on Xcode 7.3.1
For me this was the easiest solution:
Add new script build phase before Copy Bundle Resources step
Shell: /usr/bin/env python
Content:
#! /usr/bin/env python
import os
from AppKit import NSMutableDictionary
# Key to replace
settings_key = 'version_preference' # the key of your settings version
# File path
settings_path = os.environ.get('SRCROOT') + "/TheBeautifulNameOfYourOwnApp/Settings.bundle/Root.plist"
# Composing version string
version_string = os.environ.get('MARKETING_VERSION') + " (" + os.environ.get('CURRENT_PROJECT_VERSION') + ")"
# Reading settings plist
settings_plist = NSMutableDictionary.dictionaryWithContentsOfFile_(settings_path)
for dictionary in settings_plist['PreferenceSpecifiers']:
if 'Key' in dictionary and dictionary['Key'] == settings_key:
dictionary['DefaultValue'] = version_string
# Save new settings
settings_plist.writeToFile_atomically_(settings_path, True)
With Xcode 11.4, you can use the following steps to display the app version in your application's settings bundle.
$(MARKETING_VERSION) and $(CURRENT_PROJECT_VERSION) variablesNote: if $(MARKETING_VERSION) and $(CURRENT_PROJECT_VERSION) variables appear for Bundle version string (short) and Bundle version keys in Info.plist, you can skip the following steps and jump to the next section.
0.1.0) and change the Build field content to some new value (e.g. 12). These 2 changes will create $(MARKETING_VERSION) and $(CURRENT_PROJECT_VERSION) variables in Info.plist file.<?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>PreferenceSpecifiers</key>
<array>
<dict>
<key>DefaultValue</key>
<string></string>
<key>Key</key>
<string>version_preference</string>
<key>Title</key>
<string>Version</string>
<key>Type</key>
<string>PSTitleValueSpecifier</string>
</dict>
</array>
<key>StringsTable</key>
<string>Root</string>
</dict>
</plist>
version="$MARKETING_VERSION"
build="$CURRENT_PROJECT_VERSION"
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:0:DefaultValue $version ($build)" "${SRCROOT}/Settings.bundle/Root.plist"

These are the variables I had to use for a swift project with Xcode 12.2
version=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$PROJECT_DIR/$INFOPLIST_FILE")
build="$CURRENT_PROJECT_VERSION"
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:0:FooterText Version $version" "$CODESIGNING_FOLDER_PATH/Settings.bundle/ServerURLSettings.plist"
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:0:FooterText Version $version($build)" "$CODESIGNING_FOLDER_PATH/Settings.bundle/DeveloperSettings.plist"