0%

(译)Debug,Beta,App Store版本的同时构建

前言

以前开发App的时候就两个版本,调试版和发布版,至于这两个版本的区别大家都知道,服务器的数据库不同(即URL不同),那个时候我的做法就是,在网络请求API里面,将URL定义成宏,版本上线时手动切换URL。当时没有觉得这个方法很挫,后面到新东家的时候,发现有三个版本,即测试版、企业版、AppStore版,我当时就在想如果还像以前那样搞,这该怎么玩啊。后面发现三个版本生成的APP名字都不一样,我当时很惊讶,这个看上去很牛逼的样子啊。直到前段时间借助一篇博文才搞懂其中的秘密,下面我来翻译一下这篇博文,下面我采取中英文并茂的形式展现,如有翻译不当之处,请拍砖。


Concurrent Debug, Beta and App Store Builds

Debug,Beta,App Store版本的同时构建

Whilst I was developing avTag I started using HockeyApp to distribute builds to beta testers. As part of that process I decided to follow their advice and configure my project so that I could have debug, beta and App Store builds all installed on devices concurrently. You then have access to the three states of your application quickly and easily. However I found some of the configuration notes slightly confusing and although Andreas Linde gave me some very handy notes I’ve decided to write things up in a more agnostic way. I wholeheartedly recommend HockeyApp but realise that some of you are either tied in to things like TestFlight or simply don’t use a distribution service.

当我开发avTag的时候,我开始使用HockeyApp发布beta测试版给测试者。在这过程中,我听从他们的意见,配置我的项目,这样我就可以有debug,beta和AppStore版本同时安装在设备上面。然后你可以迅速容易的得到你程序的三种状态。不过,我发现其中的配置说明有些轻微的混淆,尽管Andreas Linde给我一些非常便利笔记 ,我决定以不可知论(即什么都不知道)的方法写一些东西。我衷心地建议HockeyApp要意识到一些要绑定的东西要么像 TestFlight一样或者根本不使用分发服务。


The Theory

理论

The theory behind what we are doing is very simple. Each application on your device needs to have a unique bundle identifier. If two apps share the same bundle identifier then one will over-write the other. All we are going to do to create three versions of our application is give each its own bundle identifier.

我做这个的理论非常简单。你设备上面的每一个应用程序都有一个唯一的包标识符。如果两个应用程序共享相同的包标识符,那么一个会覆盖另外一个。所以我们要做的是创建三个版本的应用程序,每个应用程序都有不同的包标识符。


Application Identifiers & Provisioning Profiles

应用程序标识符和配置文件

If your application isn’t doing anything with iCloud, Game Center or any of the other services which require unique bundle identifiers then you could use a wildcard application identifier:
com.mycompany.ios.myapp*

如果你的应用程序没有使用像iCloud,Game Center或者其他其他需要使用唯一包标识符的服务,那么你可以使用一个通用的应用程序标识符:
com.mycompany.ios.myapp*

If however you are using a service which requires unique bundle identifiers then your must create three explicit application identifiers such as:

1
2
3
com.mycompany.ios.myapp
com.mycompany.ios.myapp.debug
com.mycompany.ios.myapp.adhoc

但是如果你使用了需要用到唯一包标识符的服务,那么你必须得创建三个明确标识符,如:

1
2
3
com.mycompany.ios.myapp
com.mycompany.ios.myapp.debug
com.mycompany.ios.myapp.adhoc

Not all of you will add the ‘ios’ element but I tend to so that I can also create Mac equivalents if I need to.
不是所有的标识符你都要添加‘ios’元素,但是我倾向于这样做,因为如果我需要的话我还可以创建相对于的‘Mac’元素。

‘debug’ and ‘adhoc’ can also be anything you want. Just make sure you can remember what type of build they refer to.
‘debug’ 和 ‘adhoc’也可以指向其他你想要指向的东西。只要你确定你能记住它们指向哪种类型即可。

The ‘clean’ identifier is the one that will be used for the App Store version.
没有后缀名的标识符是用于指向App Store版本的。

Whilst the wildcard identifier is okay, since you still need to create three provisioning profiles (one for development, one for ad hoc releases and one for App Store releases) I tend to create three unique application identifiers. It also means that if you do then retrospectively add a service which needs unique bundle identifiers it’s easy to do.
通用标识符虽然很好,因为你还需要创建三个配置文件(一个用于development,一个用于ad hoc releases,一个用于App Store releases),所以我倾向于创建三个唯一的标识符。这也意味着如果你回顾头来添加需要唯一标识符的服务时,这样很容易做到。

After setting up your application identifiers you need to create the provisioning profiles. As mentioned above, you can either create development, ad hoc and release ones for your wildcard application identifier or create one development one for com.mycompany.ios.myapp.debug, an ad hoc distribution one for com.mycompany.ios.myapp.adhoc and an App Store one for com.mycompany.ios.myapp.
设置好你的应用程序标识符后,你还得创建配置文件。正如上面提到的,你可以用通用标识符创建development,ad hoc,App Store发布版本的其中一个,或者三个版本每个版本一个标识符。

Add those provisioning profiles to Xcode and you’re ready to start configuring your project.
添加这些配置文件到Xcode里面后你可以准备开始配置你的工程了。


Project Configuration

工程配置

Select the Project Navigator and then click on your project at the top to show the project and target details. Select the project and then the Info tab. You need to add a new configuration so click on the plus button and duplicate the Release configuration. Call it something like ‘Ad Hoc’ or something else which will help you identify its purpose.
选择工程导航栏然后点击顶部查看project和target详情。选择project的info选项,你需要添加新的configuration,所以你得点击添加按钮并且复制Release configuration。我们叫它‘Ad Hoc’或者别的名字,能够帮你明确它的用途。

原图

Next select your target in Xcode and then switch to the Info tab. You need to modify the bundle identifier and the bundle display name.
然后选择target的info选项,你需要去修改标识符和显示名称。

Your bundle identifier should be something like ‘com.mycompany.ios.myapp’ or ‘com.mycompany.ios.${PRODUCT_NAME:rfc1034identifier}’. This is fine for the App Store provisioning profile but it won’t work for your other two provisioning profiles.
你的包标识符应该是像‘com.mycompany.ios.myapp’ 或者 ‘com.mycompany.ios.${PRODUCT_NAME:rfc1034identifier}’。这对App Store的配置文件非常好,但是它不会对另外两个配置文件起作用。

Edit the bundle identifier so that it reads:
com.mycompany.ios.myapp${BUNDLE_ID_SUFFIX}
or
com.mycompany.ios.${PRODUCT_NAME:rfc1034identifier}${BUNDLE_ID_SUFFIX}
像下面一样修改标识符
com.mycompany.ios.myapp${BUNDLE_ID_SUFFIX}
或者
com.mycompany.ios.${PRODUCT_NAME:rfc1034identifier}${BUNDLE_ID_SUFFIX}

Next change the bundle display name. It’s hopefully currently ${PRODUCT_NAME}. This simply means that the product name is gleaned from the build settings. Change it to:
${PRODUCT_NAME}${BUNDLE_DISPLAY_NAME_SUFFIX}
然后修改包的显示名称。目前希望是${PRODUCT_NAME},这就意味着你的工程名称是来源于编译设置。
改变它:
${PRODUCT_NAME}${BUNDLE_DISPLAY_NAME_SUFFIX}


原图

Switch to the Build Settings tab and click on the Add Build Settings button in the bottom right and select ‘Add User-Defined Setting’. Call the new setting ‘BUNDLE_ID_SUFFIX’. Add a second user-defined setting and call it ‘BUNDLE_DISPLAY_NAME_SUFFIX’. These are the two values you entered in the Info tab.
切换到‘Build Settings’,点击右下角的‘Add Build Settings’按钮,选择‘Add User-Defined Setting’。叫新的设置为‘BUNDLE_ID_SUFFIX’,添加第二个user-defined设置并且叫它为‘BUNDLE_DISPLAY_NAME_SUFFIX’。这里有你在Info选项卡输入的两个值。

Expand both and you will see that you can enter values for each of the three configurations. You’re not going to add anything to the Release configurations but you do need to change the Debug and Ad Hoc ones.
展开它们两,你会发现你可以向这三个配置输入值。你不会添加任何东西到Release配置,但是你需要去修改Debug和Ad Hoc。

The BUNDLE_ID_SUFFIX needs to match the bundle identifiers you set up in the Apple Developer centre so enter ‘.debug’ against the Debug configuration and ‘.adhoc’ against the Ad Hoc one (note the leading periods). This means that when you build your application these values are appended to the bundle identifier so you end up with:

1
2
3
com.mycompany.ios.myapp.debug for the Debug configuration.
com.mycompany.ios.myapp.adhoc for the Ad Hoc configuration.
com.mycompany.ios.myapp for the Release configuration.

BUNDLE_ID_SUFFIX需要去匹配你你在苹果开发者中心设置的包标识符,所以输入‘.debug’匹配Debug配置,输入‘.adhoc’匹配Ad Hoc配置(注意开始创建的时候)。这就意味着,当你创建应用时,这些值是附加到你的包标识符的,所以你可以这样理解:

1
2
3
com.mycompany.ios.myapp.debug for the Debug 配置.
com.mycompany.ios.myapp.adhoc for the Ad Hoc 配置.
com.mycompany.ios.myapp for the Release 配置.

The BUNDLE_DISPLAY_NAME_SUFFIX is totally subjective. I would suggest adding something short. I use alpha and beta symbols for debug and ah hoc releases respectively. Remember to add a space before the suffix too.
BUNDLE_DISPLAY_NAME_SUFFIX完全是主观的。我会建议添加一些短的值。我用α和β符号分别代表debug版本和ah hoc版本。记住在后缀前面添加了个空格。

原图

At this point you should configure the Code Signing Entitlements too and match the Debug, Ad Hoc and Release configurations to their respective provisioning profiles. This is the only place where using a wildcard application identifier comes in handy since you can leave Xcode to automatically pick the correct provisioning profile.
在这个点上,你应该配置代码签名并且匹配Debug,Ad Hoc 和 Release版本它们各自的配置文件。通用应用程序标志就派上用场了,因为可以让Xcode自动选择正确的配置文件。


Schemes

方案
You need to do some tinkering with the schemes next so select the ‘Product > Scheme > Manage Schemes…’ menu item. The idea is that you have two schemes; one for ad hoc builds and one for App Store builds. Your debug configuration is shared across them both.
你需要用schemes做一些修补,点击‘Product > Scheme > Manage Schemes…’菜单项。这个想法就是,你有两个schemes,一个是ad hoc builds,另外一个是App Store builds,你的debug configuration共享他们两个。

The schemes list probably only shows one item so duplicate it and call the duplicate something like ‘MyApp Beta’. Run and Test should use the Debug configuration, whilst Profile and Archive should use the Ad Hoc configuration.
这个方法列表里面很有可能只显示一个项目,以便复制它并调用复制对象类似于‘MyApp Beta’,运行和测试使用Debug配置,而Profile和Archive应该使用Ad Hoc配置。

Then edit the original scheme. Select it in the Manage Schemes sheet and then click it again so that you can change the name to something like ‘MyApp App Store’. Then edit it and check that Run and Test use the Debug configuration and that Profile and Archive use the Release configuration.
然后编辑最初的scheme,选择Manage Schemes,然后再次点击它,以便你能将它的名字改成类似‘MyApp App Store’样。然后编辑它并且使用Debug配置检查 Run 和 Test,使用Release配置检查Profile 和 Archive。

You can now select either scheme in Xcode which means that you can build debug versions of your application, build a beta version for distribution by doing an archive build of the Ad Hoc scheme or build an App Store version by doing an archive build of the App Store scheme.
现在,你可以在Xcode选择任何一种scheme,这意味着你可以构建你应用程序的测试版本,构建通过Ad Hoc scheme 生成的beta版本,构建通过 App Store scheme生成的App Store版本.


Build Numbers

编译号

Version and build numbers tend to be a bit subjective. The approach I use is this…
版本号和编译号一般都比较主观性。我用的方法是这样的…

For pre-release development before the application is in the App Store I increment the version number (‘Bundle versions string, short’ in the Info.plist file) whenever I release a new build to the beta testers. This is something like 0.1 or 0.2.3. The build number (‘Bundle version’ in the Info.plist file) is also incremented each time I release a new build to the beta testers.
对于将要发布到AppStore的预发布版本,我增加版本号(在Info.plist文件的‘Bundle versions string, short’项)当我发布一个新的beta版。这个有点像0.1或者0.2.3。编译号在Info.plist文件的‘Bundle version’项)也会增加。

The first version to be submitted to the App Store will be version 1 and might have a build number of 27.
提交到AppStore第一个版本的版本号是1,编译号可能是27。

Once the application is in the App Store the version number only increased when a new version is released through the store. Here the build number becomes more important since this still increases each time I release a build to the beta testers.
一旦应用程序在App Store已发布,当你再提交新版本的时候,这个版本号只能增加。此时,编译号就变得功能重要,因为我每发布一个beta测试版编译号就得增加。

So, imagine that version 1 of the application is in the store with a build number of 27. A feature is added and the beta testers get a new build to test. This is version 1.1, build 28. Two more beta versions are released to testers so the version which is submitted to the
App Store is 1.1 build 30.
所以,想象一下当第一次发布在AppStore的应用版本号是1,编译号是27。当增加一个功能后发布beta版本的时候版本号会增加。这是版本号为1.1,编译号为28。当我在发布两次beta测试版后再提交到AppStore的时候版本号为1.1,编译号为30。

The next beta build would be 1.2 build 31 if it added a feature or, if it is a minor bug release then it would be 1.1.1 build 31.
我增加一个新功能,发布下一个测试版的时候版本号为1.2,编译号31。或者我解决了一个bug发布时版本号为1.1.1,编译号31。

The important thing is that the version number increments for App Store releases and the build number increments for beta releases.
所以重要的事情是发布到AppStore的时候版本号增加,发布beta测试版的时候编译号增加。

On this basis it would be good if the build number auto-incremented each time you did a beta build. For that I use a slightly modified Build Phase script from Stack Overflow:
在此基础上,如果我每次编译时编译号自动增加是非常好的。于是,我用了一个可以改变编译阶段的脚本(来自Stack Overflow)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if [ $CONFIGURATION == "Ad Hoc" ]; then
echo "Bumping build number..."
plist=${PROJECT_DIR}/${INFOPLIST_FILE}

# increment the build number
buildnum=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${plist}")
if [[ "${buildnum}" == "" ]]; then
echo "No build number in $plist"
exit 2
fi

buildnum=$(expr $buildnum + 1)
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "${plist}"
echo "Bumped build number to $buildnum"

else
echo $CONFIGURATION " build - Not bumping build number."
fi

The only things you have to remember are:

  • Increase the version number manually. I tend to do this immediately after releasing a version so that I don’t forget.
  • Manually increase the build number if you didn’t do any ad hoc builds for a release.

You could negate number two by running the script for Ad Hoc and Release configurations. As I say, it’s all subjective.

你唯一要记住的事是:

  • 手动增加版本号。我倾向于发布一个版本后马上增加,这样我就不会忘记。
  • 手动增加编译号。如果你发布的时候没有做任何编译。

你可以通过在Ad Hoc 和 Release 配置上面运行脚本来否定第二点。正如我说的,这都是主观的。

There is a gotcha however. The scripts is run after Xcode has read the values from the plist file if your current build number is 27 and you do a beta build then the beta application will have a build number of 27. Xcode then ends up with a build number of 28 ready and waiting. Basically, consider the shown build number to be the build number of the version you are next going to release. Thanks to Spencer MacDonald for reminding me to add this.

但是这有一个问题。脚本运行后,Xcode已经从plist文件中读取值,假设你现在的编译号是27,并且你将要发布的beta版本的编译号也是27。然后Xcode会以28的编译号准备一个版本准备。基本上,考虑显示的版本号将成为你下个将要发布版本的版本号。感谢Spencer MacDonald 提醒我增加这个。


Badging The Icon

图标的标记号
Another way you can improve this process is to badge the actual application icons with build numbers. Richard Stelling kindly emailed me some sample code for this.
另一种能够改善这个过程的方法是在实际版本的图标上面添加标记号。Richard Stelling 好心的发邮件,关于这个的实例代码。

The first thing to note is that the method Richard suggests uses a private API so you don’t want the code included in your App Store build. You can use conditional compilation to achieve this.
要注意的第一件事是,Richard 建议我用私有的API,这样你不用考虑在AppStore版本写代码。你可以通过条件编译来实现这个。

The first step is to add some values to the three configurations for the Preprocessor Macros in the target’s Build Settings. The three values are:
第一步是像三种配置添加一些值,这些值用来表示在target编译设置里面的预处理宏。这三个值是:

1
2
3
CONFIGURATION_DEBUG
CONFIGURATION_ADHOC
CONFIGURATION_RELEASE

These can be seen in-situ in the following screenshot:
这些可以从下面的截图中复原:

原图

The ‘DEBUG=1’ item is probably already there, inserted by Xcode.
‘DEBUG=1’这一项很有可能由于被Xcode插入从而已经出现在这里。

What this means is that in your code you can now use conditional compilation to include or exclude code from builds based on their configuration.
这就意味着,在你的代码里面,你可以使用条件编译包含或者排除基于配置的代码。

1
2
3
4
5
6
7
8
9
10
11
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#if defined (CONFIGURATION_DEBUG)
[[UIApplication sharedApplication] performSelector:@selector(setApplicationBadgeString:) withObject:@"α"];
#elif defined (CONFIGURATION_ADHOC)
[[UIApplication sharedApplication] performSelector:@selector(setApplicationBadgeString:) withObject:@"β"];
#elif defined (CONFIGURATION_RELEASE)
// Don't do the badging. That'll get you rejected!
#endif

...


原图

The end result of all of this is that your device will be able to show something like this:
在你设备上面最终的显示结果是这样的:

原图

Evan Doll talked about something similar in his great talk about iOS Tools at Flipboard which he gave at NSConference 5 this year. You can get a video of Evan’s talk from the NSConference web site. Evan’s approach is to actually change the icon’s image and add version information to it. This is a good idea if you don’t have version information available somewhere within the application itself.
Evan Doll 在今年的NSConference 5大会上有谈论类似的关于iOS工具Flipboard。你可以从 NSConference 网站找到关于Evan谈话的视频。Evan的方法是改变应用的图标并且添加版本信息。如果你没有可用的版本信息在应用程序里面的话,这将是一个好主意。


URL Schemes

URL 方案
Simon Harris pointed out that if you implement custom URL schemes then you need to make sure that you include some way of differentiating between the app types.
Simon Harris指出如果你要实现自定义的URL方案,你需要确保你应用程序中包含有区分应用类型的方法。


Bootstrapping with KZBootstrap

Krzysztof Zabłocki has created an open source project called KZBootstrap which automates an awful lot of what I’ve talked about above and it is definitely worth a look.
Krzysztof Zabłocki 创建了一个 KZBootstrap的开源项目,它能够自动化很多我们上面提到的东西,它非常值得你一看。


Acknowledgements

感谢

This article was based on two main sources:

Additional thanks to:

这篇文章主要参考:

另外还得感谢:


总结

第一次翻译国外博文,感觉还是很吃力,大概花了两个晚上,5个小时左右的样子,oh!fuck!这效率貌似有点低哈,下次得加油。感觉回到了中学英语阅读理解的时代,不过现在是在自己熟悉的领域,其实很多东西我都不知道该怎么交,只知道大概个意思,像上面提到的scheme,我知道是方案的意思,就是你编译的时候选择不同的方案,但是用中文翻译成“方案”感觉不是很靠谱,后面在Apple上找到了。
An Xcode scheme defines a collection of targets to build, a configuration to use when building, and a collection of tests to execute.
原来Xcode Scheme它定义了一系列构建的target,编译时的配置,已经要执行的测试集合。

WTF?! Scheme刚弄懂,target又来了…..
A target specifies a product to build and contains the instructions for building the product from a set of files in a project or workspace. A target defines a single product; it organizes the inputs into the build system—the source files and instructions for processing those source files—required to build that product. Projects can contain one or more targets, each of which produces one product.
target就是将目标构建好并包含指令,该指令用于从一个项目或工作区的一组文件的构建产品。一个项目可以有多个target,每个target生成一个产品。

有时我们项目会有两个target,用于区分AppStore版本和企业版本,所以你创建文件的时候,你会勾选这两个target,其实他们的代码和资源都是一样的,只是证书不同,就像国外博文说的那样。