93

我想在我的应用程序的设置包中包含应用程序版本和内部修订,例如 1.0.1 (r1243)。

Root.plist 文件包含这样的片段...

     <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>

我想在构建时替换“VersionValue”字符串。

我有一个可以从我的存储库中提取版本号的脚本,我需要的是一种在构建时处理(预处理)Root.plist 文件的方法,并在不影响源文件的情况下替换修订号。

4

13 回答 13

71

还有另一种解决方案可能比之前的任何一个答案都简单得多。Apple 在其大多数安装程序中捆绑了一个名为PlistBuddy的命令行工具,并将其包含在 Leopard 的/usr/libexec/PlistBuddy.

由于您要替换VersionValue,假设您将版本值提取到$newVersion,您可以使用以下命令:

/usr/libexec/PlistBuddy -c "Set :VersionValue $newVersion" /path/to/Root.plist

无需摆弄 sed 或正则表达式,这种方法非常简单。有关详细说明,请参见手册页。您可以使用 PlistBuddy 添加、删除或修改属性列表中的任何条目。例如,我的一个朋友写了一篇关于使用 PlistBuddy在 Xcode 中增加内部版本号的博客。

注意:如果您只提供 plist 的路径,PlistBuddy 会进入交互模式,因此您可以在决定保存更改之前发出多个命令。我绝对建议在将其放入构建脚本之前执行此操作。

于 2009-06-30T05:22:16.593 回答
66

我懒人的解决方案是从我的应用程序代码中更新版本号。您可以在 Root.plist 中有一个默认(或空白)值,然后在您的启动代码中的某个位置:

NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
[[NSUserDefaults standardUserDefaults] setObject:version forKey:@"version_preference"];

唯一的问题是您的应用程序必须至少运行一次,更新版本才能出现在设置面板中。

您可以更进一步地更新这个想法,例如,更新您的应用程序已启动次数的计数器,或其他有趣的信息。

于 2009-07-17T17:46:40.370 回答
65

根据@Quinn 的回答,这里是我用来执行此操作的完整过程和工作代码。

  • 将设置包添加到您的应用程序。不要重命名它。
  • 在文本编辑器中打开 Settings.bundle/Root.plist

将内容替换为:

<?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>
  • 创建一个运行脚本构建阶段,移到复制捆绑资源阶段之后。添加此代码:

    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"
    
  • 将 MyAppName 替换为您的实际应用名称,并将 PreferenceSpecifiers 后面的 1 替换为设置中您的版本条目的索引。上面的 Root.plist 示例在索引 1 处有它。

于 2012-10-11T14:55:59.413 回答
28

使用 Ben Clayton 的 plist https://stackoverflow.com/a/12842530/338986

在 . 之后添加Run script以下代码段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"

除了. CFBundleVersion_ CFBundleShortVersionString它发出这样的版本:

通过写信 $CODESIGNING_FOLDER_PATH/Settings.bundle/Root.plist 而不是写信$SRCROOT有一些好处。

  1. 它不会修改存储库工作副本中的文件。
  2. 您不需要将路径输入到Settings.bundlein $SRCROOT。路径可能会有所不同。

在 Xcode 7.3.1 上测试

于 2016-08-19T02:16:33.210 回答
13

根据此处的示例这是我用来自动更新设置包版本号的脚本:

#! /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)

这是我在 Settings.bundle 中的 Root.plist:

<?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>
于 2010-07-05T12:20:41.697 回答
12
于 2020-05-20T19:44:20.200 回答
9

由于一个原因,其他答案无法正常工作:在打包设置捆绑包之后才执行运行脚本构建阶段。因此,如果您的 Info.plist 版本是 2.0.11,并且您将其更新为 2.0.12,然后构建/归档您的项目,设置包仍会显示 2.0.11。如果您打开 Settings bundle Root.plist,您可以看到版本号在构建过程结束之前不会更新。您可以再次构建项目以正确更新设置包,或者您可以将脚本添加到预构建阶段...

  • 在 XCode 中,为您的项目目标编辑方案
  • 单击 BUILD 方案上的披露箭头
  • 然后,单击“预操作”项
  • 单击加号并选择“新建运行脚本操作”
  • 将 shell 值设置为 /bin/sh
  • 将“提供构建设置来自”设置为您的项目目标
  • 将您的脚本添加到文本区域。以下脚本对我有用。您可能需要修改路径以匹配您的项目设置:

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

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

这将在构建/归档过程中打包设置包之前正确运行脚本。如果您打开 Settings bundle Root.plist 并构建/归档您的项目,您现在将看到在构建过程开始时更新了版本号,并且您的 Settings bundle 将显示正确的版本。

于 2016-05-09T04:07:52.570 回答
3

我设法通过使用 pListcompiler ( http://sourceforge.net/projects/plistcompiler ) 开源项目来做我想做的事。

  1. 使用此编译器,您可以使用以下格式将属性文件写入 .plc 文件:

    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. 我有一个自定义运行脚本构建阶段,将我的存储库修订提取到 .h 文件,如 brad-larson所述

  3. plc 文件可以包含预处理器指令,如#define、#message、#if、#elif、#include、#warning、#ifdef、#else、#pragma、#error、#ifndef、#endif、xcode 环境变量。所以我可以通过添加以下指令来引用变量 kRevisionNumber

    #include "Revision.h"
    
  4. 我还在我的 xcode 目标中添加了一个自定义脚本构建阶段,以便在每次构建项目时运行 plcompiler

    /usr/local/plistcompiler0.6/plcompile -dest Settings.bundle -o Root.plist Settings.plc
    

就是这样!

于 2009-05-19T14:52:54.970 回答
3

我的工作示例基于@Ben Clayton 的回答以及@Luis Ascorbe 和@Vahid Amiri 的评论:

注意:此方法修改存储库工作副本中的Settings.bundle/Root.plist文件

  1. 将设置包添加到项目根目录。不要重命名

  2. 打开 Settings.bundle/Root.plist 作为 SourceCode

    将内容替换为:

    <?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. 将以下脚本添加到项目(目标)方案的 Build, Pre-actions 部分

    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. 构建并运行当前方案

于 2018-12-23T13:19:37.790 回答
3

以上答案对我不起作用,因此我创建了自定义脚本。

这会动态更新来自 Root.plist 的条目

使用下面的运行脚本。肯定会在 xcode 10.3 中验证。

“var buildVersion”是要在标题中显示的版本。

并且标识符名称是 settings.bundle Root.plist 中标题的下面的“var version”

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

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>

工作示例 Root.plist 条目

于 2019-08-28T12:49:41.017 回答
1

我相信你可以使用类似于我在这个答案中描述的方式来做到这一点(基于这篇文章)。

首先,您可以通过将 VersionValue 重命名为 ${VERSIONVALUE} 来使 VersionValue 成为 Xcode 中的变量。创建一个名为 versionvalue.xcconfig 的文件并将其添加到您的项目中。转到您的应用程序目标并转到该目标的构建设置。我相信您需要将 VERSIONVALUE 添加为用户定义的构建设置。在该窗口的右下角,将“基于”值更改为“版本值”。

最后,转到您的目标并创建一个运行脚本构建阶段。检查运行脚本阶段并将脚本粘贴到脚本文本字段中。例如,我用当前 Subversion 构建标记我的 BUILD_NUMBER 设置的脚本如下:

REV=`/usr/bin/svnversion -nc ${PROJECT_DIR} | /usr/bin/sed -e 's/^[^:]*://;s/[A-Za-z]//'`
echo "BUILD_NUMBER = $REV" > ${PROJECT_DIR}/buildnumber.xcconfig

当这些值在您的项目中发生变化时,这应该可以替换变量。

于 2009-05-18T12:54:23.677 回答
1

这些是我在 Xcode 12.2 的 swift 项目中必须使用的变量

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"
于 2020-11-19T17:21:16.047 回答
0

对我来说,这是最简单的解决方案:

在复制捆绑资源步骤之前添加新的脚本构建阶段

贝壳:/usr/bin/env python

内容:

#! /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)
于 2020-06-05T10:23:42.347 回答