您根据不一样的环境配置了哪些内容?您可能具备仅用于调试的视图,或者您可能但愿关闭发布版本的日志记录。您可能有多个后端环境可配置为 dev,QA,UAT,stage,prod 等。其中每一个都须要不一样的 root url,api key和app secret。该应用程序还可能与社交媒体,崩溃报告工具或其余分析工具集成,咱们不该该经过咱们的测试工做污染这些数据。咱们可能还但愿更改应用程序图标和应用程序名称,以使其显示已安装的应用程序正在运行的环境。shell
开发简单的 iOS 应用程序并不用担忧配置太多,这很容易。当你刚刚开始时,可使用代码进行一些设置,根据须要修改值。您甚至能够尝试注释/取消注释代码行以在不一样配置之间切换。有些人使用#if DEBUG
。这些中的任何一个都会很快成为问题。它容易出错而且很是耗时。那么咱们该怎么办?swift
让咱们来看一个设置具备多个环境的项目的示例。我只会作两个,但能够根据须要重复这些步骤。后端
您可使用现有项目或建立新项目。单个视图应用程序适用于此演示。我称之为“EnvironmentsTest”。默认状况下,您将拥有一个 scheme 和两个 configurations(debug和release)。让咱们从一个结构开始,以保存咱们的可配置属性。api
struct Config {
let scheme: String
let host: String
let key: String
init() {
scheme = "https"
host = "api.testapp.com"
key = "key.testapp.prod"
}
}
extension Config {
static var current: Config = Config()
}
复制代码
为了测试这一点,咱们能够在应用程序完成启动时打印配置。注意:不要忘记删除它,您不但愿您的应用程序在生产中泄露此信息。xcode
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
print(Config.current)
return true
}
复制代码
在控制台中,您将看到指望的配置被打印出来。安全
Config(scheme: "https", host: "api.testapp.com", key: "key.testapp.prod")
复制代码
当你只有两个环境担忧时,几乎能够天然地将调试分配给 dev 并释放到 prod。我已经看过不少了。不幸的是,这不起做用。 Dev 和 prod 表示环境配置,而 debug 和 release 是构建配置。您应该可以在某种程度上混合和匹配它们。例如,您可能想要调试 prod 构建。您永远不会发布开发构建,但您可能想要分析开发构建,咱们一般使用发布配置。固然,当您想要添加更多环境时,您将彻底崩溃。bash
咱们能够在 Info.plist 文件中建立能够读入的条目来替换默认配置。app
<key>Config</key>
<dict>
<key>scheme</key>
<string>http</string>
<key>host</key>
<string>dev-api.testapp.com</string>
<key>key</key>
<string>key.testapp.dev</string>
</dict>
复制代码
而后更改 Config 扩展名以读取它。直接使用 infoDictionary 可能很诱人,特别是当咱们只有三个值时,但随着你的配置变得愈来愈复杂,拥有可解码的东西会更好。ide
struct ConfigContainer: Decodable {
let Config: Config
}
extension Config: Decodable {
static var current: Config = {
guard let infoURL = Bundle.main.url(forResource: "Info", withExtension: "plist") else { fatalError("No info.plist in main bundle") }
do {
let infoData = try Data(contentsOf: infoURL)
let decoder = PropertyListDecoder()
let item = try decoder.decode(ConfigContainer.self, from: infoData)
return item.Config
} catch {
return Config()
}
}()
}
复制代码
如今咱们能够看到开发环境的配置。工具
Config(scheme: "http", host: "dev-api.testapp.com", key: "key.testapp.dev")
复制代码
因此,如今咱们经过 info.plist 注入配置,这是朝着正确方向迈出的一步。如今,您如何在这些环境之间切换?咱们须要一种方法来定义全部配置,而后可以在它们之间进行选择。
当开发人员但愿在同一设备上安装多个版本(不一样环境)的应用程序时,他们一般会添加 target。大多数状况下,添加target 是多余的。添加 target 时,您必须记住设置每一个 target 的设置。例如,若是要在应用程序中启用摄像头访问,则必须单独为每一个 target 设置所需的 Info.plist。此外,不管什么时候向项目添加文件,都必须确保将其正确添加到每一个 target。
咱们真正追求的是从下拉列表中选择环境/配置并运行它。schemes 是一种很好的方法。首先,咱们须要设置一些构建配置。
咱们从 debug 和 release 配置开始。让咱们将这些与咱们的生产环境相结合。只需将 Debug 重命名为 Prod-Debug 并将其发布到 Prod-Release。接下来,建立一个新配置,复制 Prod-Debug。重命名新配置 Dev-Debug。
在大多数状况下,您不须要复制发布版本。为每一个环境执行调试和发布会变得混乱且难以维护。我建议只为您要分析或分发的环境添加发布配置。
如今已经创建了构建配置,咱们能够建立 scheme。单击方案下拉列表,而后选择 “Edit Scheme…”
选择左下角的**“Duplicate Scheme”**按钮。命名新方案以指明target和环境;我称之为“EnvironmentsTest - Dev”.
确保 scheme 是选中 shared 的。即便您不与团队合做,也能够确保在移动到另外一台计算机时保存设置。 对于左侧的每种类型的构建**(Run, Test, Profile, Analyze)** ,选择Info选项卡,而后将Build Configuration设置为Dev-Debug。 若是您为此环境建立了发布配置,则应将其用于 Profile。在 Debug 配置中进行性能分析的后果超出了本文的范围,在某些状况下能够,只知道在 Release 中可能会有不一样的结果。 我老是把 Archive 设置为 Prod-Release。这样,不管选择何种方案,archive build都将为 Prod 构建。而后我没必要担忧意外上传 Dev 版本。最终,最好依靠其余工做流程工具来防止这样的错误,但这是另外一话题了。
完成后按关闭按钮。
此时,咱们添加了构建配置和scheme,以便为不一样的环境构建。可是,若是您运行该应用程序,则每一个环境都会获得相同的结果。咱们如何实际定制它?
配置设置文件是设置每一个环境、配置设置的好地方。转到File -> New -> File… 或按⌘N。向下滚动到“Other”部分,而后选择Configuration Settings File。按Next并将其命名为 Dev.xcconfig 和Create。输入或复制如下内容:
scheme = http
host = dev-api.testapp.com
key = key.testapp.dev
复制代码
而后返回到 Info.plist 文件并使用如下内容替换 Config 部分。
<key>Config</key>
<dict>
<key>scheme</key>
<string>$(scheme)</string>
<key>host</key>
<string>$(host)</string>
<key>key</key>
<string>$(key)</string>
</dict>
复制代码
最后,返回建立项目配置的位置。您会注意到右栏是Based on Configuration File。修改 Dev-Debug 项目以使用Dev配置。
如今,当您使用 Dev 方案运行项目时,您将看到 Dev 配置的结果,当您使用 Prod 方案运行项目时,您将看到一个空配置。那么,咱们应该为 Prod 构建建立另外一个配置设置文件。可是,因为曝光,我不喜欢这种解决方案。我建议咱们以不一样的方式处理 prod。
若是您要将其分发给公众,Info.plist 不是放置任何应用程序机密的正确位置。所以,咱们须要找到另外一种配置 Prod 的方法。真正保护这些值超出了本文的范围,但一个好的开始是将这些值从 Info.plist 中移出并转换为已编译的代码。若是你回顾咱们的Config
结构,它已经存在了。咱们所要作的就是从 Info.plist 中删除(空)值,代码应加载咱们的默认配置,即Prod。
从 Project Navigator 中选择项目,选择目标并转到Build Phases选项卡。单击**+按钮,而后选择“New Run Script Phase”**。单击标题将其重命名为不太通用的名称。我将标题设置为“Remove Prod Config”,将如下内容复制到脚本区域。
PLISTBUDDY="/usr/libexec/PlistBuddy"
INFO_PLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
if [[ -z ${host} ]]; then
$PLISTBUDDY -c "Delete :Config" "${INFO_PLIST}" || true
fi
复制代码
这就是说,若是“host”变量为空(或不存在),则从 Info.plist 文件中删除 Config 对象。
注意:您必须执行**clean build(⌘⇧K)**以强制build phase运行。
如今,您将在每一个 scheme 中得到正确的结果! 🎉
您可能已经意识到,Apple 如今须要应用程序来支持最佳实践 HTTPS 安全性。对于每一个环境都遵循此规则一般是个好主意,即便在开发中也是如此。可是,您不可能老是这样作(例如,在准备“本地”环境时)或者可能还没有配置,但您仍须要继续开发。咱们能够经过另外一个Run Script Phase处理这个问题。将此命名为“Http - Allows Arbitrary Loads”并将如下内容复制到脚本区域。
PLISTBUDDY="/usr/libexec/PlistBuddy"
INFO_PLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
if [ ${scheme} == "http" ]; then
$PLISTBUDDY -c "Add :NSAppTransportSecurity dict" "${INFO_PLIST}"
$PLISTBUDDY -c "Add :NSAppTransportSecurity:NSAllowsArbitraryLoads bool true" "${INFO_PLIST}" || true
$PLISTBUDDY -c "Set :NSAppTransportSecurity:NSAllowsArbitraryLoads true" "${INFO_PLIST}"
else
$PLISTBUDDY -c "Delete :NSAppTransportSecurity:NSAllowsArbitraryLoads" "${INFO_PLIST}" || true
fi
复制代码
经过在application(_:didFinishLaunchingWithOptions:)
打印值来测试每一个 scheme.
print(String(describing: Bundle.main.infoDictionary?["NSAppTransportSecurity"]))
复制代码
很是好,不是吗?
有时在同一设备上安装Dev build和Prod build很方便,这样你就能够在二者之间切换。要实现这一点,您所要作的就是更改包ID。您还须要一种简单的方法来肯定哪个是哪一个。为此,咱们能够更改显示名称和图标。
注意:对于全部这些设置,我始终保持生产版本的构建;这是进入App Store的构建,不该修改。因为这个规则,它也很容易识别,由于它没有任何修饰符。
要更改Bundle Identifier,请转到 target 的build settings并搜索“bundle identifier”,而后单击箭头以展开全部配置。我一般会附加一个“ - ”后跟配置名称。
这些步骤与Bundle Identifier相同,但此次搜索“product name”。为此,我建议用环境替换名称。不然,文本可能太长而没法使用。
更改图标几乎一样容易。在同一屏幕上,搜索“Icon”。您应该看到“Asset Catalog App Icon Set Name.”。再次,添加一个破折号,后跟配置名称。
每一个配置执行全部这些步骤彷佛须要作不少工做才能更改一些设置,但这很是值得。固然,它须要一些时间来完成全部设置,但完成后切换变得微不足道。花时间作正确的事,从长远来看,你将节省时间。您能够经过在环境之间快速切换来节省时间。你能够省去手动操做时犯错误的麻烦。