没有目标的Xcode文件变体

如果您想使用其他替代配置来配置iOS / Mac(Swift或Objective-C)构建,则可以遵循许多不同的方法。 通常,您可以将它们分为两个集群:在运行时(例如,解析Info.plist )或编译时(利用Swift编译条件/预处理器宏或定义新目标)进行切换。 显然,我们总是更喜欢编译时检查,但是开销经常会夸大其好处。

这篇文章将提出一种基于Xcode构建规则(与传统的Xcode目标相反)来控制文件变体选择的替代方法。

问题陈述

有很多解决方案可以构建不同风格的应用程序,这些应用程序可能在以下方面有所不同:

  • 端点地址(例如分期与生产)
  • 逻辑(例如,启用/禁用地理围栏检查)

有关更多详细信息,请允许我推荐这篇文章,其中详细介绍了其中的大多数内容。
长话短说,对于Swift开发,您可以选择:

  • 使用#if DEBUG ... #endif结构来包含或禁用给定代码块的编译条件
  • 单独的目标 ,其中每个目标都包含一个单独的专用文件,其中包含针对给定配置的特定代码
  • .plist输入文件或环境变量进行运行时检查

每个都有一个缺点,请提及:

  • 如果影响编译器的条件,可能会导致代码过多,导致代码混乱
  • 单独的目标引入了不必要的需求,将所有“共享”代码库和配置都包含在其中
  • 对于运行时配置,我们会丢失编译时检查。

如果我们可以有一个目标 ,该目标根据配置使用文件的特定变体,那不是很好吗?

解决方案:使用自定义构建规则使用或跳过.swift文件

传统上,Xcode目标使您可以有选择地选择要编译的文件。 您可以利用它来用另一个指定其他baseURL或完全不同的逻辑策略的源文件替换一个源文件。 为了这篇文章的缘故,让我们假设我们要使用两个不同的Configuration_X.swift文件之一:

  // Configuration_S.swift 
结构配置{
让baseUrl =“ https://staging.example.com/”
} // Configuration_P.swift
结构配置{
让baseUrl =“ https://example.com/”
}

没有目标,我们可以使用Xcode项目中不太流行的自定义“构建规则”来实现,您可以在其中根据文件名指定如何处理项目文件。

添加自定义构建规则时,必须指定文件模式,要应用的Shell脚本以及脚本的输出文件。 然后,Xcode在编译过程中将评估您的脚本中与给定模式匹配的文件,而不是默认行为(例如,编译.swift文件)。 请记住,自定义构建规则优先于嵌入规则-这使我们有机会覆盖默认的编译行为。

对于我们的解决方案,我们将遵循以下算法:

  • Configuration_X.swift 所有版本都包含在一个目标中
  • Xcode项目指定了一个虚拟的构建规则 ,该规则吞下了您要跳过的文件(从构建中排除)
  • 照常处理所有其他文件(格式为xxxxx_x.swift

为了控制要在虚拟构建规则中吞噬的文件,我们将使用用户定义的设置CONFIGURATION_VARIANT (设置名称取决于您)来创建单独的Xcode配置:

上面的配置意味着对于“调试”配置,我们希望选择带有“ S”后缀( xxxxx_S.swift )的登台文件变体,以及对于带有“ P”后缀( xxxxx_P.swift )的“发行”产品。

有很多其他选项可以指定用户定义的构建设置,例如xcodebuild终端命令的`buildsetting = value`参数。

回到我们新创建的构建规则,让我们指定我们要手动处理项目.swift文件的子集:

  1. 处理以CONFIGURATION_VARIANT以外的单字符后缀结尾的Swift文件(请注意,Xcode在这里使用简单的模式匹配,而不是更强大的正则表达式)
  2. 该脚本在派生目录中创建带有_skipped后缀的空文件
  3. 指定Xcode应该处理刚刚创建的空文件,而不是原始的文件-从技术上讲,这里我们将文件的主体视为空文件。

请注意,我们在$DERIVED_FILE_DIR目录上进行操作,因此给定脚本可以在构建目录中创建文件,并且不会修改原始文件(可能在源代码控制下)。

结果是:不再有多个目标,不再有#if... #endif块,也没有运行时检查—构建设置(例如,特定于配置)控制是否应包含给定文件。

可以在 GitHub 找到 演示该应用程序的示例应用程序

最后,请注意,此技术不仅限于.swift文件。 通过对上述构建规则进行少量修改,您也可以将其应用于Objective-C文件,图像,资产或其他数据(例如.json文件)。

摘要

构建规则是iOS / Mac开发人员经常低估且几乎不使用的工具。 这篇文章演示了在编译过程中选择性地包括/排除.swift文件如何有用。 现在,在沉浸于多目标项目或基于条件的代码的世界之前,我们还需要考虑另一种选择。

一个限制是我们只能使用单字符后缀标记(例如_A.swift,_B.swift,_1.swift等),因为Xcode在Build Rules选项卡中不支持glob或regex文件匹配。 随时复制 OpenRadar 建议,该建议使Build Rules对此具有更多控制权。