xcode 如何在应用程序的设置包中显示应用程序版本修订?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/877128/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 03:02:37  来源:igfitidea点击:

How can I display the application version revision in my application's settings bundle?

iphonexcodeapplication-settingssettings-bundle

提问by Panagiotis Korros

I would like to include the application version and internal revision, something like 1.0.1 (r1243), in my application's settings bundle.

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

The Root.plist file contains a fragment like this...

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>

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

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

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.

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

采纳答案by Panagiotis Korros

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

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

  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
    
  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 here 所述

  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
    

And that was it!

就是这样!

回答by Quinn Taylor

There is another solution that can be much simpler than either of the previous answers. Apple bundles a command-line tool called PlistBuddyinside most of its installers, and has included it in Leopard at /usr/libexec/PlistBuddy.

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

Since you want to replace VersionValue, assuming you have the version value extracted into $newVersion, you could use this command:

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

/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 pagefor 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 Xcodeusing PlistBuddy.

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

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.

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

回答by Quinn Taylor

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:

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

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.

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

回答by Ben Clayton

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

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

  • Add a settings bundle to your app. Don't rename it.
  • Open Settings.bundle/Root.plist in a text editor
  • 向您的应用程序添加设置包。不要重命名。
  • 在文本编辑器中打开 Settings.bundle/Root.plist

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 Scriptbuild phase, move to be after the Copy Bundle Resourcesphase. 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.

  • 创建运行脚本构建阶段,移至复制捆绑资源阶段之后。添加此代码:

    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 处有它。

回答by hiroshi

Using Ben Clayton's plist https://stackoverflow.com/a/12842530/338986

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

Add Run scriptwith following snippet after Copy Bundle Resources.

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"

Appending CFBundleVersionin addition of CFBundleShortVersionString. It emit version like this:

追加CFBundleVersion另外的CFBundleShortVersionString。它发出这样的版本:

By writing to $CODESIGNING_FOLDER_PATH/Settings.bundle/Root.plistinstead of the one in $SRCROOThave some benefits.

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

  1. It dosen't modify files in working copy of repository.
  2. You don't need to case path to Settings.bundlein $SRCROOT. The path may vary.
  1. 它不会修改存储库工作副本中的文件。
  2. 您不需要将路径添加到Settings.bundlein $SRCROOT。路径可能会有所不同。

Testing on Xcode 7.3.1

在 Xcode 7.3.1 上测试

回答by mrwalker

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:

这是我在 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>

回答by Andy

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

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

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

  • 在 XCode 中,为您的项目目标编辑方案
  • 单击 BUILD 方案上的披露箭头
  • 然后,点击“Pre-actions”项
  • 单击加号并选择“新建运行脚本操作”
  • 将 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"

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.

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

回答by Peter Kreinz

My working Example based on @Ben Clayton answer and the comments of @Luis Ascorbe and @Vahid Amiri:

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

Note: This approach modifies the Settings.bundle/Root.plistfile in working copy of repository

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

  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

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

回答by dheeraj_jha

Above answers did not work for me hence I created my custom script.

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

This dynamically updates the entry from Root.plist

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

Use run script below. W ill work for sure verified in xcode 10.3.

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

"var buildVersion" is the version to be displayed in title.

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

And identifier name is "var version" below for title in settings.bundle Root.plist

标识符名称是 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

Example entry in Root.plist

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

工作示例 Root.plist 条目

回答by Brad Larson

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

首先,您可以通过将 VersionValue 重命名为 ${VERSIONVALUE} 来使 VersionValue 成为 Xcode 中的一个变量。创建一个名为 versionvalue.xcconfig 的文件并将其添加到您的项目中。转到您的应用程序目标并转到该目标的构建设置。我相信您需要将 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:

最后,转到您的目标并创建一个运行脚本构建阶段。检查运行脚本阶段并在脚本文本字段中粘贴您的脚本。例如,我使用当前 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

This should do the trick of replacing the variable when these values change within your project.

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