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
Above answers did not work for me hence I created my custom script.
This dynamically updates the entry from Root.plist
Use run script below. W ill work for sure verified in xcode 10.3.
"var buildVersion" is the version to be displayed in title.
And identifier name is "var version" below for title in settings.bundle Root.plist
cd "${BUILT_PRODUCTS_DIR}"
#set version name to your title identifier's string from settings.bundle
var version = "Version"
#this will be the text displayed in title
longVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${INFOPLIST_PATH}")
shortVersion=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" ${TARGET_BUILD_DIR}/${INFOPLIST_PATH})
buildVersion="$shortVersion.$longVersion"
path="${WRAPPER_NAME}/Settings.bundle/Root.plist"
settingsCnt=`/usr/libexec/PlistBuddy -c "Print PreferenceSpecifiers:" ${path} | grep "Dict"|wc -l`
for (( idx=0; idx<$settingsCnt; idx++ ))
do
#echo "Welcome $idx times"
val=`/usr/libexec/PlistBuddy -c "Print PreferenceSpecifiers:${idx}:Key" ${path}`
#echo $val
#if ( "$val" == "Version" )
if [ $val == "Version" ]
then
#echo "the index of the entry whose 'Key' is 'version' is $idx."
# now set it
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:${idx}:DefaultValue $buildVersion" $path
# just to be sure that it worked
ver=`/usr/libexec/PlistBuddy -c "Print PreferenceSpecifiers:${idx}:DefaultValue" $path`
#echo 'PreferenceSpecifiers:$idx:DefaultValue set to: ' $ver
fi
done
Example entry in Root.plist
<dict>
<key>Type</key>
<string>PSTitleValueSpecifier</string>
<key>Title</key>
<string>Version</string>
<key>DefaultValue</key>
<string>We Rock</string>
<key>Key</key>
<string>Version</string>
</dict>
Based on the example here, here's the script I'm using to automatically update the settings bundle version number:
#! /usr/bin/env python
import os
from AppKit import NSMutableDictionary
settings_file_path = 'Settings.bundle/Root.plist' # the relative path from the project folder to your settings bundle
settings_key = 'version_preference' # the key of your settings version
# these are used for testing only
info_path = '/Users/mrwalker/developer/My_App/Info.plist'
settings_path = '/Users/mrwalker/developer/My_App/Settings.bundle/Root.plist'
# these environment variables are set in the XCode build phase
if 'PRODUCT_SETTINGS_PATH' in os.environ.keys():
info_path = os.environ.get('PRODUCT_SETTINGS_PATH')
if 'PROJECT_DIR' in os.environ.keys():
settings_path = os.path.join(os.environ.get('PROJECT_DIR'), settings_file_path)
# reading info.plist file
project_plist = NSMutableDictionary.dictionaryWithContentsOfFile_(info_path)
project_bundle_version = project_plist['CFBundleVersion']
# print 'project_bundle_version: '+project_bundle_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'] = project_bundle_version
# print repr(settings_plist)
settings_plist.writeToFile_atomically_(settings_path, True)
Here's the Root.plist I've got in Settings.bundle:
<?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>1.0.0.0</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>
There is another solution that can be much simpler than either of the previous answers. Apple bundles a command-line tool called PlistBuddy inside most of its installers, and has included it in Leopard at /usr/libexec/PlistBuddy
.
Since you want to replace VersionValue
, assuming you have the version value extracted into $newVersion
, you could use this command:
/usr/libexec/PlistBuddy -c "Set :VersionValue $newVersion" /path/to/Root.plist
No need to fiddle with sed or regular expressions, this approach is quite straightforward. See the man page for detailed instructions. You can use PlistBuddy to add, remove, or modify any entry in a property list. For example, a friend of mine blogged about incrementing build numbers in Xcode using PlistBuddy.
Note: If you supply just the path to the plist, PlistBuddy enters interactive mode, so you can issue multiple commands before deciding to save changes. I definitely recommend doing this before plopping it in your build script.
I managed to do what I wanted by using the pListcompiler (http://sourceforge.net/projects/plistcompiler) open source porject.
Using this compiler you can write the property file in a .plc file using the following format:
plist {
dictionary {
key "StringsTable" value string "Root"
key "PreferenceSpecifiers" value array [
dictionary {
key "Type" value string "PSGroupSpecifier"
key "Title" value string "AboutSection"
}
dictionary {
key "Type" value string "PSTitleValueSpecifier"
key "Title" value string "Version"
key "Key" value string "version"
key "DefaultValue" value string "VersionValue"
key "Values" value array [
string "VersionValue"
]
key "Titles" value array [
string "r" kRevisionNumber
]
}
]
}
}
I had a custom run script build phase that was extracting my repository revision to .h file as described by brad-larson here.
The plc file can contain preprocessor directives, like #define, #message, #if, #elif, #include, #warning, #ifdef, #else, #pragma, #error, #ifndef, #endif, xcode environment variables. So I was able to reference the variable kRevisionNumber by adding the following directive
#include "Revision.h"
I also added a custom script build phase to my xcode target to run the plcompiler every time the project is beeing build
/usr/local/plistcompiler0.6/plcompile -dest Settings.bundle -o Root.plist Settings.plc
And that was it!
My lazy man's solution was to update the version number from my application code. You could have a default (or blank) value in the Root.plist and then, somewhere in your startup code:
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
[[NSUserDefaults standardUserDefaults] setObject:version forKey:@"version_preference"];
The only catch is that your app would have to be run at least once for the updated version to appear in the settings panel.
You could take the idea further and update, for instance, a counter of how many times your app has been launched, or other interesting bits of information.
I believe you can do this using a way that's similar to what I describe in this answer (based on this post).
First, you can make VersionValue a variable within Xcode by renaming it to ${VERSIONVALUE}. Create a file named versionvalue.xcconfig and add it to your project. Go to your application target and go to the Build settings for that target. I believe that you need to add VERSIONVALUE as a user-defined build setting. In the lower-right-corner of that window, change the Based On value to "versionvalue".
Finally, go to your target and create a Run Script build phase. Inspect that Run Script phase and paste in your script within the Script text field. For example, my script to tag my BUILD_NUMBER setting with the current Subversion build is as follows:
REV=`/usr/bin/svnversion -nc ${PROJECT_DIR} | /usr/bin/sed -e 's/^[^:]*://;s/[A-Za-z]//'`
echo "BUILD_NUMBER = $REV" > ${PROJECT_DIR}/buildnumber.xcconfig
This should do the trick of replacing the variable when these values change within your project.