Xcode 的 Target 区分开发和生产环境

一些初学者可能会好奇,为什么在开发应用的时候我们需要用两套隔离的数据库和环境。这是因为在你持续地开发应用或增加新特性的时候,可能希望将开发版本和已经存在的生产版本的应用进行区分。标准的开发实践是针对不同版本的软件使用不同的环境,而对我们来说,这个软件就是 iPhone 应用。一般来讲,开发版应用使用的数据库(或其它系统,比如说统计分析系统)应该与生产版应用进行区分。在测试或者开发环境中,我们经常使用到类似 “test comment”, “argharghargh” 和 “one more test comment” 这样的测试数据。很显然,我们并不想让真实的用户看到这样的信息。如果应用使用了统计分析系统,在测试阶段我们可能会发送成千上万次的统计事件。我们当然也不想让这样的数据跟真实的统计数据混在一起。这就是为什么我们应该区分开发环境和生产环境。

当使用不同的环境开发时,应用需要知道它当前使用的环境。一种通用的做法是在程序的 AppDelegate 中定义一个可以将应用初始化为开发或者生产模式的全局变量。

enumenvironmentType{

casedevelopment, production

}

letenvironment:environmentType= .production

switchenvironment {

case.development:

//setweb service URLtodevelopment

//setAPIkeystodevelopment

print("It's for development")

case.production:

//setweb service URLtoproduction

//setAPIkeystoproduction

print("It's for production")

}

这种方法要求我们在切换环境的时候改变全局变量的值。虽然这种方法很方便也很快捷,但是它存在很大的局限性。首先,因为在开发与生产环境中都使用了同一个 bundle ID,我们就不能在同一个设备上同时安装应用的不同版本。再者,这种方法有可能导致我们不小心把开发版本的应用提交到 App Store 上。如果忘记切换全局变量的值,我们就会提交错误版本的应用。我曾经就有一次在提交应用之前忘记改变全局变量的值,结果用户就使用到了我的开发版本。这实在是太糟糕了。

在本篇文章中我将会展示一种更好的方法来区分开发和生产的构建版本。更确切地来说,我们将会在 Xcode 中创建一个开发版的 target。这个方法同时适用于新建的或现存的大型项目,所以你可以使用一个现有的项目来跟着这个教程进行实践。

使用这种方法,生产版和开发版的应用都会使用同一套基础代码,但是可以有不同的图标、bundle ID 以及不同的数据库。分发和提交应用的过程也会十分简单。更重要地,应用的测试人员和管理人员可以在同一个设备上同时安装应用的两个版本,这样他们就可以更加清楚地了解他们现在试用的是哪一个版本。

如何创建新的 Target

那么我们如何在 Xcode 当中新建一个开发版的 target 呢?我会使用我提供的示例项目 “todo” 来一步一步地进行演示。你可以使用自己的项目,并跟随这些步骤进行操作:

1.  在 Project Navigator 面板上选择项目,进入设置。在Targets小节下面,右击现有的 target 并且选择Duplicate来对一个现在的 target 进行复制。

2.  Xcode 会询问你这个 target 是不是针对于 iPad 开发的。对于本教程来说,我们只需要选择 “Duplicate Only” 就可以了。

提示:如果你的项目支持通用设置,则 Xcode 不会提示上面的消息。

3.  现在我们有了一个名为todo copy的新 target 和构建 scheme 。让我们来重命名一下以便于区分。

TARGETS列表中选择新创建的 target。按下回车键来对它的名称进行编辑,并且给它起一个合适的名称。我倾向于使用 “todo Dev”。当然你可以使用你喜欢的任何名字。

接下来,来到 “Manage Schemes…”,选择在第 1 步中新建的 scheme,并按下回车键。把新 scheme 命名为跟新 target 一样的名字(�即我们上一小步中使用的名字)

4.  这一步是可选的,不过我强烈建议进行这一步。如果想让开发版跟生产版的应用更加容易区分,我们应该提供不同的应用图标和启动界面。这可以使测试更加清楚他们现在使用的版本,并且可以防止我们提交开发版本的应用到商店中。 :grinning:

选中Assets.xcassets然后添加新的 App 图标。选择 icon > App Icons & Launch Images > New iOS App Icon. 将新图标命名为 “AppIcon-Dev” 并且添加你需要的图片。

5.  现在回到项目设置,选择开发版的 target 然后修改 bundle ID。可以简单地在原来的 ID 上添加一个 “Dev” 后缀。如果有操作过第 4 步,在这里确保你的 app 图标设置为在上一步中添加的图标。

6.  Xcode 会自动为新的 target 创建一个 plist 文件(一般命名为 todo copy-Info.plist)。可以在项目的根目录下找到这个文件。将 “copy” 修改为 “Dev”,然后将它放到原来的 plist 文件下方。这样可以更方便我们对文件进行操作。

7.  现在选择开发版 target 下的 “Build Settings”,滚动到 “Packaging”,然后修改指定 plist 文件为开发版 plist(即刚刚的 todo Dev.plist)。

8.  最后,为生产版和开发版 target 同时设置一个预处理宏和编译器标志。这样在之后的开发中我们就可以在代码中使用这个标识来检测当前运行的应用是哪个版本。

对于 Objective-C 项目,来到Building Settings,滚动到Apple LLVM 7.0 - Preprocessing。展开Preprocessor Macros并且为DebugRelease添加一个变量。对于开发版 target(即 todo Dev),设置变量的值为DEVELOPMENT=1(校对注:等号两边不能有空格)。相对地,设置开发版 target 的值为DEVELOPMENT=0。

对于 Swift 项目,编译器不再支持预处理指令了。相对地,它使用了运行期属性(compile-time attributes)和构建配置。为了增加开发版构建的标志,选择开发 target。来到Build Settings,向下滚动到Swift Compiler - Custom Flags小节。设置值为-DDEVELOPMENT来表明当前为 target 为开发版。

现在我们已经创建并配置好了开发版的 target,接下来要做什么呢?

使用 Target 和宏

因为设置过了DEV_VERSION的宏,我们就可以在代码中利用这个宏对项目使用动态检测了。这里有一个简单的使用示例:

Objective-C:

#ifDEVELOPMENT

#defineSERVER_URL @"http://dev.server.com/api/"

#defineAPI_TOKEN @"DI2023409jf90ew"

#else

#defineSERVER_URL @"http://prod.server.com/api/"

#defineAPI_TOKEN @"71a629j0f090232"

#endif

在 Objective-C 当中,我们可以使用#if来对DEVELOPMENT的状态进行检测,并且为 URL/API 设置对应的值。

Swift:

#ifDDEVELOPMENT

letSERVER_URL="http://dev.server.com/api/"

letAPI_TOKEN="DI2023409jf90ew"

#else

letSERVER_URL="http://prod.server.com/api/"

letAPI_TOKEN="71a629j0f090232"

#endif

在 Swift 当中,我们依然可以使用#if来对构建配置进行动态判断。然而,我们不再使用#define来定义一个常量,而是简单地使用let来定义一个 Swift 中全局常量。

提示:一般情况下,我们会将上面的代码放在 AppDelegate 当中。不过最终取决于你想在哪里初始化应用的配置。

当选择 “todo Dev” 的 scheme 并运行项目,我们就会创建一个开发版的构建并自动将服务器配置设置为开发版的环境。你现在可以放心地上传这个开发版到 TestFlight 或者 HockeyApp 让应用的测试人员或者管理人员进行测试了。

以后如果需要创建一个生产版的构建,只要简单地选择 “todo” 的 scheme。一行代码都不需要改变。

关于管理多个 Target 的一些注意事项

1. 当为项目添加新文件时,不要忘记同时勾选多个 target 来保持各个 target 中的代码一致。

2.  如果你的项目中使用了 Cocoapods,不要忘记在 podfile 中增加新的 target。我们可以使用link_with来指定多个 target。可以在Cocoapods 文档中找到更详细的内容。现在 Podfile 看起来应该是这样的:

source'https://github.com/CocoaPods/Specs.git'

platform:ios,'7.0'

workspace'todo'

link_with'todo','todo Dev'

pod'Mixpanel'

pod'AFNetworking'

如果使用了持续集成系统,比如Travis CIJenkins,不要忘了每个 target 的构建和交付都要配置。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容