Xcode Build 配置文件

原文:Xcode Build Configuration Files

软件开发最佳实践 规定了 配置与代码的严格分离。然而,Apple 平台上的开发人员通常很难将这些准则与 Xcode 的项目繁琐的工作流程相结合。
了解每个项目设置的功能以及它们之间如何交互,是一项需要多年磨练的技能。然而很多信息都被深藏在Xcode的GUI中,事实上这对我们没有帮助。
导航到项目编辑器的 “Build Settings” tab,你会看到分布在 project、target 和 configuration 上的 数百条 Build Setting —— 更不用说其他六个 tab 了!
xcconfig-project-build-settings.png

幸运的是,有一个更好的办法,不必点击迷宫般的 tab 和箭头,就可以管理所有的配置。

本周,我们将向您展示如何使用基于文本的 xcconfig文件 从 Xcode 外部化构建设置,以使您的项目更加紧凑,易懂且功能强大。

请访问XcodeBuildSettings.com以获取最新版本的Xcode支持的每个构建设置的完整参考。

Xcode Build 配置文件 他们的 xcconfig 文件扩展名更加众所周知。允许在不使用 Xcode 的情况下声明和管理应用程序的构建设置。它们是纯文本格式,这意味着它们对代码管理系统更加友好,并且可以使用任何编辑器进行修改。
从根本上讲,每个配置文件都由一系列键值分配组成,这些键值分配具有以下语法:

BUILD_SETTING_NAME = value

例如,你可以使用下面这样的 SWIFT_VERSION Build Setting,指定项目的 Swift 语言版本:

SWIFT_VERSION = 5.0

根据posix 标准,环境变量的名称仅由大写字母,数字和下划线(_)组成,这是一个惯例,我喜欢称之为SCREAMING_SNAKE_CASE 🐍🗯.

乍一看,xcconfig 文件与.env 文件具有惊人的相似之处,它们具有简单的换行符分隔的语法。但是,Xcode构建配置文件不仅仅让人眼前一亮。看哪!

保留现有值

要附加而不是替换现有定义,请使用$(inherited)变量,如下所示:

BUILD_SETTING_NAME = $(inherited) additional value

通常,您这样做是为了建立值列表,例如编译器在其中搜索框架以查找包含的头文件(FRAMEWORK_SEARCH_PATHS)的路径:

FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)

Xcode按以下顺序分配继承的值(从最低优先级到最高优先级):

  • Platform Defaults(平台默认值)
  • Xcode Project File Build Settings(Xcode 项目文件的 Build Setting)
  • xcconfig File for the Xcode Project(Xcode 项目的 xcconfig 文件)
  • Active Target Build Settings(Active Target 的 Build Setting)
  • xcconfig File for the Active Target(Active Target 的 xcconfig 文件)

空格用于分隔字符串和路径列表中的项。指定包含空格的项时,必须用引号(")括起来。

引用其他值

你可以按照下面的语法,通过其他设置的名字引用它们的值:

BUILD_SETTING_NAME = $(ANOTHER_BUILD_SETTING_NAME)

这种引用既可以用于根据现有值定义新变量,也可以用于以内联方式动态构建新值。

OBJROOT = $(SYMROOT)
CONFIGURATION_BUILD_DIR = $(BUILD_DIR)/$(CONFIGURATION)-$(PLATFORM_NAME)

设置参考的构建设置的默认值

在Xcode 11.4和更高版本中,可以使用默认的评估运算符来指定回退值,如果引用的构建设置的评估结果为空,则使用该替代值。

$(BUILD_SETTING_NAME:default=value)

条件编译设置

您可以根据以下语法根据其SDK(sdk),architecture(arch)和/或configuration(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"

正如我们将在本文后面看到的那样,您可以利用此功能以非常强大的方式构建构建设置的级联列表。

正常来说,当遇到一个无法解析的 #include 指令时,编译器会报错。但是 xcconfig 文件同时也支持 #include? 指令,在该指令下,若文件无法找到,编译器不会报错。
根据文件是否存在而改变编译时行为的情况并不多;毕竟,Build 最好是可预见的。但是你可以把它用在可选的开发工具上,比如 Reveal 需要以下的配置:

# Reveal.xcconfig
OTHER_LDFLAGS = $(inherited) -weak_framework RevealServer
FRAMEWORK_SEARCH_PATHS = $(inherited) /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries

创建 Build 配置文件

要创建 Build 配置文件,请选择 “File > New File…” 菜单项(⌘N),下拉到 “Other” 部分,选中 Configuration Settings File 模板。将它保存到你的项目目录,并确保它在你期望的 target 上。

xcconfig-new-file--dark.png

创建好 xcconfig 文件后,你就可以将它分配给对应 target 的一个或多个 Build 配置。

xcconfig-project-configurations--dark.png

现在我们已经介绍了 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.staging.example.com")!
#else
let apiBaseURL = URL(string: "https://api.example.com")!
#endif

这只是完成了任务,但是与代码 / 配置分离的标准相冲突。

另一个方案是将这些与环境相关的值放到它们该待的地方 —— xcconfig 文件中。

// Development.xcconfig
API_BASE_URL = api.example.dev
API_KEY = 9e053b0285394378cf3259ed86cd7504

---

// Production.xcconfig
API_BASE_URL = api.example.com
API_KEY = 4571047960318d233d64028363dfa771

不幸的是,xcconfig 将所有 // 都当成注释分隔符,不管它们是否包括在引号中。如果你用反斜线 \/\/ 进行转义,那么这些反斜线也将被直接展示出现,使用时必须从结果中移除。在指定每个环境的 URL 常量时,这尤其不方便。

如果不想处理这种麻烦的事情,你可以在 xcconfig 中忽略 scheme,然后在代码中添加 https://。_(你是在使用 https……对吧?)_

然而,要以编程方式获取这些值,我们还需要一个额外的步骤:

在 Swift 中访问 Build Setting

由 Xcode 项目文件、xcconfig 文件和环境变量定义的 Build Setting 只在 Build 时可用。当你运行一个已经编译的 APP 时,所有相关的上下文都是不可见的。_(谢天谢地!)_

但是等一下——你不记得之前在其他 tab 中看到过一些 Build Setting 吗?Info,是吗?

实际上,Info tab 只是 target 的 Info.plist 文件的一个马甲。Build 时,这个 Info.plist 文件会根据 Build Setting 的配置进行编译,然后复制到最终 APP 的 bundle 中。因此,添加 $(API_BASE_URL) 和 $(API_KEY) 的引用后,你可以通过 Foundation Bundle API 的 infoDictionary 属性访问这些值。完美!

xcconfig-project-info-plist--dark.png

按照这种方法,我们可以做如下工作:

import Foundation

enum Configuration {
    enum Error: Swift.Error {
        case missingKey, invalidValue
    }

    static func value<T>(for key: String) throws -> T where T: LosslessStringConvertible {
        guard let object = Bundle.main.object(forInfoDictionaryKey:key) else {
            throw Error.missingKey
        }

        switch object {
        case let value as T:
            return value
        case let string as String:
            guard let value = T(string) else { fallthrough }
            return value
        default:
            throw Error.invalidValue
        }
    }
}

enum API {
    static var baseURL: URL {
        return try! URL(string: "https://" + Configuration.value(for: "API_BASE_URL"))!
    }
}

从调用的角度考虑,我们发现这种方法与我们的最佳实践完美地在结合一起 —— 没有出现一个硬编码的常量!

let url = URL(string: path, relativeTo: API.baseURL)!
var request = URLRequest(url: url)
request.httpMethod = method

请勿使用xcconfig文件存储API密钥或其他凭据之类的机密。有关更多信息,请参阅我们的文章secret management on iOS

Xcode 项目是庞大、脆弱的和不透明的。它们是团队成员之间进行协作的摩擦之源,并且通常是工作上的累赘。

幸运的是,xcconfig文件可以解决这些难题。将配置移出Xcode并移入xcconfig文件可带来许多好处,并提供一种使项目与Xcode的细节保持距离的方法,而无需离开Cupertino(注:Apple 的总部在Cupertino)认可的“幸福道路”。

本文链接:

https://www.devorz.com/index.php/archives/xcconfig.html
1 + 8 =
快来做第一个评论的人吧~