93

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 contains a fragment like this...

     <dict>
        <key>Type</key>
        <string>PSTitleValueSpecifier</string>
        <key>Title</key>
        <string>Version</string>
        <key>Key</key>
        <string>version_preference</string>
        <key>DefaultValue</key>
        <string>VersionValue</string>
        <key>Values</key>
        <array>
            <string>VersionValue</string>
        </array>
        <key>Titles</key>
        <array>
            <string>VersionValue</string>
        </array>
    </dict>

and I would like to replace the "VersionValue" string at build time.

I have a script that can extract the version number from my repository, what I need is a way to process (pre-process) the Root.plist file, at build time, and replace the revision number without affecting the source file.

Moshe
  • 57,511
  • 78
  • 272
  • 425
Panagiotis Korros
  • 10,840
  • 12
  • 41
  • 43

13 Answers13

71

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.

Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
  • 21
    It looks me a while to figure out the right way to refer to the version number in my plist; in my case it turned out to be /usr/libexec/PlistBuddy Settings.bundle/Root.plist -c "set PreferenceSpecifiers:0:DefaultValue $newversion" - hopefully that will be useful to other folks. – JosephH Oct 19 '10 at 10:57
  • Quinn Taylor, JosephH, thanks to your answers, I was able to automatically implement my apps version number in the Settings.bundle. +1 for both of you ;-) – Niko Mar 09 '12 at 16:32
  • 7
    From a custom "Run Script" build phase, I needed to include more of the path to Root.plist: /usr/libexec/PlistBuddy ${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}/Settings.bundle/Root.plist -c "set PreferenceSpecifiers:0:DefaultValue $newVersion" – Chris Vasselli Sep 09 '13 at 05:38
  • 2
    Just for completeness, here is another approach woth PListBuddy that worked for me: http://xcodehelp.blogspot.com/2012/05/add-version-number-to-settings-screen.html – koen Aug 31 '14 at 21:34
  • the most correct way is `/usr/libexec/PlistBuddy -c "Set :PreferenceSpecifiers:0:DefaultValue ${newVersion}" "${TARGET_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Settings.bundle/Root.plist"` – kambala Jan 18 '15 at 01:08
66

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.

  • 9
    This will work except if the user goes into Settings *before* ever launching your app. – Moshe Jan 27 '11 at 21:33
  • 9
    @Moshe True, but to handle this elegantly, you can simply specify a Default Value within the .plist file, perhaps something like 'Not Yet Launched' – Carlos P Jun 28 '11 at 12:29
  • 1
    although most developers probably set `CFBundleShortVersionString` and `CFBundleVersion` to the same value, `CFBundleShortVersionString` is actually what [Apple wants you to consider your **released** version](http://stackoverflow.com/a/6881469/119114), which would be what you show to users. `CFBundleVersion` could potentially be an internal version number, that you probably shouldn't show users (if it differs). – Nate Jun 12 '12 at 08:47
  • 3
    Am I missing something? This is EXACTLY what I'm doing, but the value doesn't get changed. Are you guys not using a Title property, which I believe is read only? – samson Oct 31 '12 at 18:41
  • 2
    There is also another problem when updating the application. The settings bundle would still display the old build version until the updated application is launched at least once. – Legoless Feb 19 '13 at 21:44
65

Based on @Quinn's answer, here the full process and working code I use to do this.

  • Add a settings bundle to your app. Don't rename it.
  • Open Settings.bundle/Root.plist in a text editor

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.

adib
  • 8,285
  • 6
  • 52
  • 91
Ben Clayton
  • 80,996
  • 26
  • 120
  • 129
  • I think this is the best way to go – Tony Dec 27 '12 at 18:31
  • I tried this and I see the Title value changing in my settings bundle. The title appears in InAppSettingsKit, but the value doesn't change from the initial version. The title never appears in the Settings app. I'm giving up and I'm just going to pop up a dialog when the user selects 'About' in a menu' – bugloaf Oct 30 '13 at 16:05
  • When using this method the setting is not read only. i.e. I can tap into the version number setting in the settings.app and it's editable. – motionpotion Sep 07 '14 at 15:15
  • I had problems with the escape characters in the beginning and the end when setting the version to the variable. If I remove it, everything works fine. Using Xcode 5.1 – Gustavo Barbosa Sep 08 '14 at 17:55
  • 8
    The bash script @ben-clayton put didn't work for me, so I remake it based on his answer, here is it: `buildVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${PROJECT_DIR}/${INFOPLIST_FILE}")` `/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:3:DefaultValue $buildVersion" "${SRCROOT}/Settings.bundle/Root.plist"` – Luis Ascorbe Dec 12 '14 at 15:54
  • didnt work for me the path is wrong. And in Luis's comment the path to Root.plist is missing the subfolder folder, ${SRCROOT}/AppName/Settings.bundle/Root.plist – malhal Jul 24 '15 at 13:51
  • This approach works great. I figured you don't need to escape the backwardssinglequote characters in the first line though. Also, modern Xcode projects don't use the Classes folder anymore; anyway, people should adapt the file paths as needed, every dev should know how to do this. – bio Oct 10 '15 at 13:23
  • 1
    you can use `${INFOPLIST_PATH}` for the info plist path – Tomer Even Mar 15 '16 at 12:51
  • I found it useful to not only get `CFBundleShortVersionString` but also `CFBundleVersion` so I could show the App Version and Build. – Mr Rogers May 23 '18 at 18:25
30

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.

  1. It dosen't modify files in working copy of repository.
  2. You don't need to case path to Settings.bundle in $SRCROOT. The path may vary.

Testing on Xcode 7.3.1

Community
  • 1
  • 1
hiroshi
  • 6,871
  • 3
  • 46
  • 59
  • 1
    This is the best answer IMO if you add the script to the Build, Pre-actions section of the project scheme. Look at Andy's answer. – Vahid Amiri Feb 12 '18 at 09:17
  • 2
    This is working for me. Just remember to change "DefaultValue" to be specific to you. For example, I wanted to change the footer so I used "FooterText". You also need to change the number after "PreferenceSpecifiers" so it correlates to the item in the plist. – Richard Witherspoon Mar 30 '20 at 15:35
18

With Xcode 11.4, you can use the following steps to display the app version in your application's settings bundle.


Set $(MARKETING_VERSION) and $(CURRENT_PROJECT_VERSION) variables

Note: 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.

  1. Open the Xcode project.
  2. Open Project Navigator (cmd1), select your project to reveal your project's settings then select the app target.
  3. Select the General tab.
  4. In the Identity section, change the Version field content to some new value (e.g. 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.

Create and configure Settings Bundle

  1. In the Project Navigator, select your project.
  2. Select File > New > File… (cmdN).
  3. Select the iOS tab.
  4. Select Settings Bundle in the Resource section, then click Next and Create.
  5. Select Root.plist and open it as source code. Replace its content with the code below:
<?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 a Run Script

  1. In the Project Navigator, select your project.
  2. Select the app target.
  3. Select the Build Phases tab.
  4. Click + > New Run Script Phase.
  5. Drag and drop the new phase somewhere above Copy Bundle Resources section. In this way, the script will be executed before compiling the application.
  6. Open the newly added Run Script phase and add the following script:
version="$MARKETING_VERSION"
build="$CURRENT_PROJECT_VERSION"
/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:0:DefaultValue $version ($build)" "${SRCROOT}/Settings.bundle/Root.plist"

Launch app

  1. Run the product (cmdR) on device or simulator.
  2. On the device or simulator, once the app is launched, open Settings app and select your app in the list of third-party apps. The app's version should be displayed as shown below:


Sources

Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
  • This throws an error for me `Set: Entry, "PreferenceSpecifiers:0:DefaultValue", Does Not Exist` – Jordan H May 28 '20 at 14:02
  • 4
    This worked for me: `/usr/libexec/PlistBuddy "$SRCROOT/AppName/Settings.bundle/Root.plist" -c "set PreferenceSpecifiers:0:DefaultValue $version"` – Jordan H May 28 '20 at 14:10
  • Thanks. This what world for me. But mine was named Settings-Watch.bundle and removed the `($build)` – Kurt L. Jun 30 '20 at 19:13
  • 1
    Awesome! For me the key help here was using @Ben Clayton's answer but modifying the run script to use $MARKETING_VERSION and $CURRENT_PROJECT_VERSION as you point out. This was necessary for me because those version numbers are now not actually stored directly in Info.plist, so reading Info.plist in the run script isn't helpful in this case (which is Xcode default now). – Mete Dec 03 '20 at 10:30
  • oh also, minor detail, but your run script unnecessarily redefines `$MARKETING_VERSION` -> `$version` - you can just put `$MARKETING_VERSION` directly in the PlistBuddy command, making it a one-liner. – Mete Dec 03 '20 at 10:31
  • 2
    This worked for me straight ahead, thanks! Best step to step answer!! – FrugalResolution Feb 16 '23 at 00:37
13

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>
mrwalker
  • 1,883
  • 21
  • 23
  • Very useful - I'm having trouble executing PlistBuddy from Python, and I never would have thought of using `NSDictionary` (and didn't realize it gave you such easy access to plist files, either) – Dov Jan 05 '12 at 16:25
  • Thank you for this. One modification -- as you have it now, it makes a change to the source, not the builddir -- which means that what you see on your device or in the simulator will always be one build version behind the actual build version. To fix this, I modified your script to iterate first over the source, then also the builddir, ie. settings_path_build = os.path.join(os.environ.get('TARGET_BUILD_DIR'), settings_file_path_build) – xaphod Nov 27 '14 at 16:20
  • ... and also, I append the githash: `gitHash = subprocess.check_output(["git", "rev-parse", "--short", "HEAD"]).rstrip()` – xaphod Nov 27 '14 at 16:43
9

The other answers do not work correctly for one reason: The run script build phase isn't executed until AFTER the Settings Bundle has been packaged. So, if your Info.plist version is 2.0.11 and you update it to 2.0.12, then build/archive your project, the Settings bundle will still say 2.0.11. If you open the Settings bundle Root.plist, you can see that the version number does not get updated until the END of the build process. You can build the project AGAIN to get the Settings bundle updated correctly, or you can add the script to a pre-build phase instead...

  • In XCode, Edit the Scheme for your project target
  • Click the disclosure arrow on the BUILD scheme
  • Then, click on the "Pre-actions" item
  • Click the plus sign and choose "New Run Script Action"
  • Set the shell value to /bin/sh
  • Set "Provide build settings from" to your project target
  • Add your script to the text area. The following script worked for me. You may need to modify the paths to match your project setup:

    versionString=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}")

    /usr/libexec/PlistBuddy "$SRCROOT/Settings.bundle/Root.plist" -c "set PreferenceSpecifiers:0:DefaultValue $versionString"

This will correctly run the script BEFORE the Settings bundle is packaged during the build/archive process. If you open the Settings bundle Root.plist and build/archive your project, you will now see the version number is updated at the beginning of the build process and your Settings bundle will display the correct version.

Andy
  • 123
  • 1
  • 4
  • Thank you, only your solution shows correct build version. Other solutions required to build twice. – Boris Y. Jan 20 '17 at 15:33
  • This still required a second build for me, using Xcode 10.0 – Patrick Oct 22 '18 at 07:00
  • @Patrick The iOS settings app sometimes retains old info. To see the change, you have to kill and restart the Settings app. – Andy Oct 24 '18 at 10:14
  • 1
    BTW, I've found a simpler way to add this script: Go to the Build Phases tab of your project target and click the "+" icon. Choose "New Run Script Phase" and add the script code in there. THIS IS THE KEY: click and drag the new run script to the top of the Build Phases list, under Target Dependencies, but before Compile Sources. This will behave the same as a Pre-build script and is easier to find. – Andy Oct 24 '18 at 10:24
  • Thanks @Andy, your solution of adding to the Build Phases tab worked perfectly. – Patrick Oct 31 '18 at 03:50
  • In the above post by @hirosh, they recommend using the `$CODESIGNING_FOLDER_PATH` as it doesn't impact the working copy when working locally. Is this something that would work with your way? – Patrick Oct 31 '18 at 03:52
  • Just tried it and it doesn't, wondering if there is any other way to prevent it from modifying the working copy. – Patrick Oct 31 '18 at 04:05
3

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

  1. Add a settings bundle to your project root. Don't rename it

  2. 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>
    
  3. 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"
    
  4. Build and Run the current scheme

Community
  • 1
  • 1
Peter Kreinz
  • 7,979
  • 1
  • 64
  • 49
3

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>

Working sample Root.plist entry

3

I managed to do what I wanted by using the pListcompiler (http://sourceforge.net/projects/plistcompiler) open source porject.

  1. 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
                    ]
                }
            ]
        }
    }
    
  2. I had a custom run script build phase that was extracting my repository revision to .h file as described by brad-larson here.

  3. 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"
    
  4. 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!

Community
  • 1
  • 1
Panagiotis Korros
  • 10,840
  • 12
  • 41
  • 43
  • That sounds like a lot of work just to replace a single value in a plist file... It's conceptually cool to be able to access a variable when building a plist, but much easier to use a tool build for plist file. I describe PlistBuddy in my answer — give it a shot! – Quinn Taylor Jun 30 '09 at 05:25
1

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"
Richard Witherspoon
  • 4,082
  • 3
  • 17
  • 33
1

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.

Community
  • 1
  • 1
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
  • This works if I want to embed the version number into the Info.plist file. But I can't make it work for other plist files for example the Root.plist file that is located into the Settings.bundle. Is there a build setting that I can use to enable this? – Panagiotis Korros May 18 '09 at 13:47
0

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)
gklka
  • 2,459
  • 1
  • 26
  • 53