在开始此教程之前,我们假设你已经完成了应用程序的开发和测试,现在准备提交生产发布。问题是,某些 Web 服务 URLs 指向测试服务器,而 API keys 则为测试环境而配置。在提交应用程序给苹果审核之前,你需要修改所有这些 API keys 和 URLs以适应生产环境。这很正常,对吧?但是,除了将这些值在开发和生产环境之间来回更改,是否存在更好的方法来管理开发和生产版本呢?这正是笔者将与你讨论的。

下面开始我们的教程!

首先,你们中的一些人可能想知道为什么在开发应用时,要使用两个单独的数据库和环境。原因是,随着你持续不断的构建新的功能或开发应用,你想确保开发版本和现有的生产版本相互区分。标准软件开发过程旨在针对软件(在我们的案例中,即为iPhone应用)的不同版本,使用不同的环境。开发版本的应用通常使用一个不同于生产版本的数据库(或其他系统,比如分析)。这就是为什么我们应该在不同的环境中使用不同的服务器和数据库。开发人员通常在测试期间使用虚拟图像或数据。在测试或开发环境中,通常会使用一些测试数据,比如“test comment”,“argharghargh”和“one more test comment”。很明显,你可不希望真实用户看到这样的消息。如果你的应用使用了分析系统,你可能在测试阶段发送成千上万的事件。同样,你不想在同一个数据库中混合测试数据和生产数据。这就是为什么总是推荐使用相互独立的开发和生产环境。

在使用两个独立的环境时,你的应用需要一种方法来找出它应该连接的环境。一个常用的方法是在主app delegate中定义一个全局变量,将应用初始化为开发或生产模式。

enum environmentType {
case development, production
}

let environment:environmentType = .production

switch environment {
case .development:
// set web service URL to development
// set API keys to development
print("It's for development")
case .production:
// set web service URL to production
// set API keys to production
print("It's for production")
}

这种方法需要你在每次切换环境时,改变全局变量。尽管还算快速和方便,这种方法也有一些重大的限制。首先,因为我们在开发和生产环境中使用同一个bundle ID,你不能在同一个设备上安装两个应用版本。当你想在同一台设备上测试应用的开发版本,但目前仍在使用应用的生产版本时,这就非常不方便。此外,这种方法可能会意外地将应用的开发版本发布到应用商店。如果你忘了改变单一的全局变量,你就会发布错误的应用程序。笔者记得有一次在应用提交之前,忘记改变全局变量,用户获得的就是应用的开发版本。那简直是一场灾难。

在本文中,笔者将向你展示一个更好的方法,来区分开发和生产版本。具体来说,我们将在XCode中创建一个开发Target。此方法同时适用于新的,和现有的大型项目,所以你可以使用一个现有的应用程序,来学习本教程。

通过使用这种方法,应用的生产和开发版本将具有相同的基础代码,但可以有不同的图标,bundle ID,并指向不同的数据库。发布和提交过程非常简单。最重要的是,测试人员和经理可以在同一设备上,安装应用程序的两个版本,所以他们完全清楚正在测试的是哪个版本。

如何创建一个新的Target

如何在Xcode中创建一个开发Target?笔者将使用自己的模板项目“todo”,逐步演示整个过程。你可以使用自己的项目,按部就班即可:

  1. 在Project Navigator面板中找到项目设置。在Targets区域,右键单击现有target,并选择Duplicate复制现有的target。

enter image description here

  1. Xcode会问你,新target是否为iPad而开发。对于本教程,我们选择 “Duplicate Only”。 enter image description here

Note: If your project supports universal devices, Xcode will not prompt the above message. 注意:如果你的项目支持通用设备,Xcode则不会提示上述消息。

  1. 现在,我们有了一个新的target,和一个新的构建scheme,名为todo copy。让我们对它重命名,使其更容易理解。

    • Select the new target in the TARGETS list. Press Enter to edit the text and put a more appropriate name. I prefer “todo Dev”. You’re free to choose whatever name you like.
    • 在TARGETS列表中,选择新的target。按Enter键来编辑文本,选择一个更合适的名字。笔者更喜欢“todo Dev”。你可以自由选择任何你喜欢的名字。
    • Next, go to “Manage Schemes…”, select the new scheme you created in step 1 and press “Enter”. Make the scheme name the same as the new target name (which is the one you choose for the new target.)
    • 接下来,选择“Manage Schemes…”,选择你在步骤1中创建的新scheme,按下“Enter”。使scheme名称和新的target名称一样(也就是你为新target选择的名称。)

enter image description here

  1. 步骤4是可选的,但强烈推荐。如果你想简单的区分开发和生产版本,你应该为每个版本使用单独的图标和启动屏幕。这将使你的测试人员很清楚地知道他们正在使用哪个应用,防止你发布开发版本的应用。:)

Go to Assets.xcassets and add a new icon. Right click icon > App Icons & Launch Images > New iOS App Icon. Rename the new icon to “AppIcon-Dev” and add your own images. 选择Assets.xcassets,并添加一个新的图标。右击icon> App Icons & Launch Images > New iOS App Icon。将新图标重命名为“AppIcon-Dev”,然后添加自己的图片。 enter image description here

5.现在回到项目设置,选择你的开发target,并更改bundle identifier。比如,你可以在原ID上简单地添加“Dev”。如果你执行了步骤4,确保你把应用图标设置为在上一步中创建的那个。 enter image description here

6.Xcode 将为你的target自动添加plist文件 (比如todo copy-Info.plist)。你可以在项目的根目录中找到它。将它从“copy”重命名为“Dev”,并将其保存在原始的plist文件下。这样,你就能方便管理文件。

7.现在打开开发target 的“Build Settings”,滚动到“Packaging”,将值更改为开发plist文件(比如todo Dev.plist)。 enter image description here

8.最后,我们将为生产和开发targets配置一个预处理宏/编译器标记。这样,之后我们就可以在代码中使用标记来检测正在运行的是应用程序的哪个版本。

对于Objective - C项目,选择Build Settings,滚动到Apple LLVM 7.0 - Preprocessing。展开Preprocessor Macros,添加Debug和Release fields变量。对于开发target (比如todo Dev),将值设置为DEVELOPMENT=1。换句话说,将值设置为DEVELOPMENT=0来表示生产版本。 enter image description here enter image description here

对于Swift项目,编译器不再支持预处理器指令。相反,它使用编译时属性和构建配置。添加一个标识,来标示开发版本,选择开发target。选择Build Settings和向下滚动到Swift Compiler—Custom Flags部分。将值设置为-DDEVELOPMENT来标示target为开发版本。 enter image description here

现在,你已经创建和配置完成开发target,下一步是什么?

使用target和宏

通过配置宏DEV_VERSION,我们可以在代码中使用它,为你的项目执行动态编译。下面是一个简单的例子:

Objective-C:

#if DEVELOPMENT 
#define SERVER_URL @"http://dev.server.com/api/" 
#define API_TOKEN @"DI2023409jf90ew" 
#else 
#define SERVER_URL @"http://prod.server.com/api/" 
#define API_TOKEN @"71a629j0f090232" 
#endif

In Objective-C, you can use #if to check the condition of DEVELOPMENT, and set up the URLs/API keys accordingly. 在Objective - C中,你可以使用#if检查DEVELOPMENT的情况,并相应地设置URLs/API keys。

Swift:

#if DEVELOPMENT
let SERVER_URL = "http://dev.server.com/api/"
let API_TOKEN = "DI2023409jf90ew"
#else
let SERVER_URL = "http://prod.server.com/api/"
let API_TOKEN = "71a629j0f090232"
#endif

在Swift中,你仍然可以使用#if为动态编译评估构建配置。然而,不是使用# define来定义原始常量,在Swift中,我们简单地使用let来定义全局常量。

注意:通常情况下,你应该把上面的代码放入app delegate。但实际上取决于你在哪里初始化应用的设置。

现在,当你选择“todo Dev” scheme,并运行这个项目,你将自动创建开发版本,服务器配置设置为开发环境。你现在可以向TestFlight或HockeyApp上传开发版本,供你的测试人员和管理人员进行测试。

之后,如果你需要创建一个生产版本,简单地选择“todo” scheme 即可。不需要任何代码更改。

管理多个Targets的一些提示

1.当你将新文件添加到项目时,别忘了同时选择两个Targets,保持代码在两个版本中同步。 enter image description here

2.如果你使用Cocoapods,别忘了将新的target添加到podfile。你可以使用 link_with来指定多个targets。你可以进一步查询Cocoapods documentation了解更多细节。你的podfile看起来应该像这样:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'
workspace 'todo'
link_with 'todo', 'todo Dev'
pod 'Mixpanel'
pod 'AFNetworking'

你觉得本教程如何?你是如何管理开发和生产版本的?请和我分享你的想法。

原文地址:http://www.appcoda.com/using-xcode-targets/

OneAPM Mobile Insight ,监控网络请求及网络错误,提升用户留存。访问 OneAPM 官方网站感受更多应用性能优化体验,想阅读更多技术文章,请访问 OneAPM 官方技术博客

本文转自 OneAPM 官方博客