作者:Mattt, 原文链接 ,原文日期:2019-05-13 译者: 雨谨 ;校对: numbbbbb , WAMaker ;定稿: Pancf
软件开发最佳实践 规定了 配置与代码的严格分离。然而,苹果平台上的开发人员常常难以将这些指导原则与 Xcode 繁重的项目工作流程结合起来。
了解每个项目设置的功能以及它们之间如何交互,是一项需要多年磨练的技能。但 Xcode 将大部分的这类信息都都深埋在其图形化界面中,这对我们没有任何好处。
导航到项目编辑器的 "Build Settings" tab,你会看到分布在 project、target 和 configuration 上的 数百条 Build Setting(构建配置) —— 更别说其他六个 tab 了!
幸运的是,有一个更好的办法,不必点击迷宫般的 tab 和箭头,就可以管理所有的配置。
这一周,我们将向你展示如何在 Xcode 之外,通过修改基于文本的
xcconfig
文件,让你的项目更加紧凑、易懂、强大。
Xcode Build 配置文件
,即大家所熟知的
xcconfig
文件,允许我们在不使用 Xcode 的情况下声明和管理 APP 的 Build Setting。它们是纯文本,这意味着它们对代码管理系统更加友好,而且可以被任意编辑器修改。
从根本上说,每个配置文件都由一系列键值对组成,其语法如下:
<#BUILD_SETTING_NAME#> = <#value#>
复制代码
例如,你可以使用下面这样的
SWIFT_VERSION
Build Setting,指定项目的 Swift 语言版本:
SWIFT_VERSION = 5.0
复制代码
根据 POSIX 标准 ,环境变量的名字由全大写字母、数字和下划线(
_
)组成 —— 经典例子就是SCREAMING_SNAKE_CASE
🐍🗯。
乍一看,
xcconfig
文件与
.env
文件有惊人的相似之处,它们的语法都很简单,都以换行分隔。但是,Xcode Build 配置文件的内容比表面上看到的要多。
看哪!
保留现有值
要追加新内容,而不是替换现有定义时,可以像这样使用
$(inherited)
变量:
<#BUILD_SETTING_NAME#> = $(inherited)<#additional value#>
复制代码
这么做通常是为了搭建一些值的列表,比如编译器的 framework 头文件的搜索路径(
FRAMEWORK_SEARCH_PATHS
):
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)
复制代码
Xcode 按下面的顺序对
inherited
进行赋值(优先级从低到高):
- 平台默认值(Platform Defaults)
- Xcode 项目文件的 Build Setting(Xcode Project File Build Settings)
- Xcode 项目的 xcconfig 文件(xcconfig File for the Xcode Project)
- Active Target 的 Build Setting(Active Target Build Settings)
- Active Target 的 xcconfig 文件(xcconfig File for the Active Target)
空格用于分隔字符串和路径列表中的项。指定包含空格的项时,必须用引号(
"
)括起来。
引用其他值
你可以按照下面的语法,通过其他设置的名字引用它们的值:
<#BUILD_SETTING_NAME#> = $(<#ANOTHER_BUILD_SETTING_NAME#>)
复制代码
这种引用既可以用于根据现有值定义新变量,也可以用于以内联方式动态构建新值。
OBJROOT = $(SYMROOT)
CONFIGURATION_BUILD_DIR = $(BUILD_DIR)/$(CONFIGURATION)-$(PLATFORM_NAME)
复制代码
条件约束
使用以下语法,你可以按 SDK(
sdk
)、架构(
arch
)和 / 或配置(
config
)对 Build Setting 进行条件约束:
<#BUILD_SETTING_NAME#>[sdk=<#sdk#>] = <#value for specified sdk#>
<#BUILD_SETTING_NAME#>[arch=<#architecture#>] = <#value for specified architecture#>
<#BUILD_SETTING_NAME#>[config=<#configuration#>] = <#value for specified configuration#>
复制代码
如果需要在同一 Build Setting 的多个定义之间进行选择,编译器将根据条件约束进行解析。
<#BUILD_SETTING_NAME#>[sdk=<#sdk#>][arch=<#architecture#>] = <#value for specified sdk and architectures#>
<#BUILD_SETTING_NAME#>[sdk=*][arch=<#architecture#>] = <#value for all other sdks with specified architecture#>
复制代码
例如,你可以使用下面这条 Build Setting 指定仅编译 active architecture,从而提升本地 Build 的速度。
ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES
复制代码
引用其他配置文件中的设置
跟
C
语言的
#include
指令一样,Build 配置文件也可以使用这种语法来引用其他配置文件中的设置。
#include "<#path/to/File.xcconfig#>"
复制代码
正如我们将在本文后面看到的,你可以利用这一点,以非常强大的方式搭建起 Build Setting 的级联列表。
正常来说,当遇到一个无法解析的
#include
指令时,编译器会报错。但是xcconfig
文件同时也支持#include?
指令,在该指令下,若文件无法找到,编译器不会报错。
根据文件是否存在而改变编译时行为的情况并不多;毕竟,Build 最好是可预见的。但是你可以把它用在可选的开发工具上,比如 Reveal 需要以下的配置:
> # Reveal.xcconfig 复制代码
OTHER_LDFLAGS =
(inherited) /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries
创建 Build 配置文件
要创建 Build 配置文件,请选择 "File > New File..." 菜单项( ⌘ N ),下拉到 "Other" 部分,选中 Configuration Settings File 模板。将它保存到你的项目目录,并确保它在你期望的 target 上。
创建好
xcconfig
文件后,你就可以将它分配给对应 target 的一个或多个 Build 配置。
现在我们已经介绍了 Xcode Build 配置文件使用的基础知识,那么让我们来看几个示例,看看如何使用它们来管理 development、stage 和 production 环境。
为内部版本提供自定义的 APP 名称和图标
开发 iOS 应用程序时,通常需要在模拟器和测试设备上安装各种内部版本(同时也会安装应用程序商店的最新版本,以供参考)。
使用
xcconfig
文件,你可以轻松地为每个配置分配一个不同的名称和 APP 图标。
// Development.xcconfig
PRODUCT_NAME = $(inherited) α
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Alpha
---
// Staging.xcconfig
PRODUCT_NAME = $(inherited) β
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Beta
复制代码
管理不同环境下的常量
如果你的后端开发人员也遵循前面提到的 12 Factor App 理论,那么他们将为 development、stage 和 production 环境提供单独的接口。
iOS 上最常见的环境管理方式可能就是使用条件编译语句 +
DEBUG
这样的 Build Setting 了。
import Foundation
#if DEBUG
let apiBaseURL = URL(string: "https://api.example.dev")!
let apiKey = "9e053b0285394378cf3259ed86cd7504"
#else
let apiBaseURL = URL(string: "https://api.example.com")!
let apiKey = "4571047960318d233d64028363dfa771"
#endif
复制代码
这只是完成了任务,但是与代码 / 配置分离的标准相冲突。
另一个方案是将这些与环境相关的值放到它们该待的地方 ——
xcconfig
文件中。
// Development.xcconfig
API_BASE_URL = api.example.dev
API_KEY = 9e053b0285394378cf3259ed86cd7504
---
// Production.xcconfig
API_BASE_URL = api.example.com
API_KEY = 4571047960318d233d64028363dfa771
复制代码