xcconfig 文件配置文件 问题

与公司 QA 聊天,已不止一次被吐槽说移动端从开发环境转到生产环境时,还要靠修改代码来配置对应的环境参数。她认为,从 App 转测试之后,就不应该再修改代码,可以把所有的环境配置都整合到配置文件中,这样打不同环境下的安装包时,会自动选择对应的环境参数。这里说到的环境参数包括但不仅限于: webservice 地址,友盟 AppKey,极光推送 AppKey 和是否是生产环境标志等。

其实,我也讨厌修改环境参数啊,?

为达成上述目的,主要是使用 Xcode 的 Configurations Setting File(即后缀为 xcconfig 文件) 来配置开发不同阶段下的环境。本文包含的内容如下:

  1. Xcode Target
  2. Xcode Project
  3. Build Setting的继承关系
  4. 如何使用xcconfig文件来配置不同开发阶段的环境

包含了一些与 build settings 相关的知识。

Xcode Target

target, 官方文档如下解释:

A target specifies a product to build and contains the instructions for building the product from a set of files in a project or workspace. A target defines a single product; it organizes the inputs into the build system—the source files and instructions for processing those source files—required to build that product. Projects can contain one or more targets, each of which produces one product.

target 定义了生成的唯一 product, 它将构建该 product 所需的文件和处理这些文件所需的指令集整合进 build system 中。Projects 会包含一个或者多个 targets,每一个 target 将会产出一个 product.

The instructions for building a product take the form of build settings and build phases, which you can examine and edit in the Xcode project editor. A target inherits the project build settings, but you can override any of the project settings by specifying different settings at the target level. There can be only one active target at a time; the Xcode scheme specifies the active target.

这些指令以 build setting 和 build phases 的形式存在,你可在 Xcode 的项目编辑器(TARGETS->Build Setting, TARGETS->Build Phases)中进行查看和编辑。target 中的 build setting 参数继承自 project 的 build settings, 但是你可以在 target 中修改任意 settings 来重写 project settings,这样,最终生效的 settings 参数以在 target 中设置的为准. Project 可包含多个 target, 但是在同一时刻,只会有一个 target 生效,可用 Xcode 的 scheme 来指定是哪一个 target 生效.

A target and the product it creates can be related to another target. If a target requires the output of another target in order to build, the first target is said to depend upon the second. If both targets are in the same workspace, Xcode can discover the dependency, in which case it builds the products in the required order. Such a relationship is referred to as an implicit dependency. You can also specify explicit target dependencies in your build settings, and you can specify that two targets that Xcode might expect to have an implicit dependency are actually not dependent. For example, you might build both a library and an application that links against that library in the same workspace. Xcode can discover this relationship and automatically build the library first. However, if you actually want to link against a version of the library other than the one built in the workspace, you can create an explicit dependency in your build settings, which overrides this implicit dependency.

target 和其生成的 product 可与另一个 target 有关,如果一个 target 的 build 依赖于另一个 target 的输出,那么我们就说前一个 target 依赖于后一个 target .如果这些 target 在同一个 workspace 中,那么 Xcode 能够发现这种依赖关系,从而使其以我们期望的顺序生成 products.这种关系被称为隐式依赖关系。同时,你可以显示指定 targets 之间的依赖关系,并且这种依赖关系会覆盖 Xcode 推测出的隐式依赖关系。

指定 targets 之间的依赖关系的地方在 Project Editor->TRAGETS->Build Phases->Target Dependencies 处设置。如下图所示:

添加targets间的依赖关系

Xcode Project

官方文档的解释如下:

An Xcode project is a repository for all the files, resources, and information required to build one or more software products. A project contains all the elements used to build your products and maintains the relationships between those elements. It contains one or more targets, which specify how to build products. A project defines default build settings for all the targets in the project (each target can also specify its own build settings, which override the project build settings).

Xcode project 是一个仓库,该仓库包含了所有的文件,资源和用于生成一个或者多个 software products 的信息。它包含一个或者多个 targets,其中的每一个 target 指明了如何生成 products。project 为其拥有的所有 targets 定义了默认的 build settings,当然,每一个 target 能够制定其自己的 build settings,且 target 的 build settings 会重写 project 的 build settings。

Xcode project 文件包含以下信息:

  • 源文件的引用:
    • 源码,包括头文件和实现文件
    • 内部和外部的库或者框架
    • 资源文件
    • 图片文件
    • Interface Builder(nib)文件
  • 文件结构导航中用来组织源文件的组
  • Project-level build configurations.你可以为 project 指定多个 build configuration,例如,project 中默认包含 debug 和 release 两种 build settings.
  • Targets, 每一个 target 指定了:
    • project 生成的 product
    • 生成 product 所需的源文件
    • 生成 product 所需的配置文件,包括对其他 targets 的依赖以及一些其他设置;当 targets 的 build configurations 没有重写 project-level 的 build settings 时,会直接使用 project-level 的 build setting.
  • 可执行环境,该环境用于调试或者测试程序,每个可执行环境会指定:
    • 运行或者调试程序时加载的可执行程序
    • 传递给可执行程序的命令行参数
    • 运行程序时需设置的环境变量

project 可独立存在,也可被包含在 workspace 中。

Build Setting 的继承关系

官方文档内容如下:

A build setting is a variable that contains information about how a particular aspect of a product’s build process should be performed. For example, the information in a build setting can specify which options Xcode passes to the compiler.

You can specify build settings at the project or target level. Each project-level build setting applies to all targets in the project unless explicitly overridden by the build settings for a specific target.

build setting 中包含了 product 生成过程中所需的参数信息。你可以在 project-level 和 target-level 层指定 build settings。project-level 的 build settings 适用于 project 中的所有targets,但是当 target-level 的 build settings 重写了 project-level 的 build settings,以 target-level 中的 build settings 中的值为准。

Each target organizes the source files needed to build one product. A build configuration specifies a set of build settings used to build a target’s product in a particular way. For example, it is common to have separate build configurations for debug and release builds of a product.

一个 build configaration 指定了一套 build settings 用于生成某一 target 的 product,例如,在 Xcode 创建项目时默认就有两套独立的 build configarations, 分别用于生成 debug 和 release 模式下的 product。

In addition to the default build settings provided by Xcode when you create a new project from a project template, you can create user-defined build settings for your project or for a particular target. You can also specify conditional build settings. The value of a conditional build setting depends on whether one or more prerequisites are met. This mechanism allows you to, for example, specify the SDK to use to build a product based on the targeted architecture.

除了创建工程时生成的默认 build settings,你也可以自定义 project-level 或者 target-level 的 build settings.

关于继承关系,The Unofficial Guide to xcconfig files 这里也有详细的说明,强烈建议阅读。

现在就来看看如何使用自定义的 build settings 来达到本文开始处提到的需求.

如何使用 xcconfig 文件来配置不同开发阶段的环境

目前公司中的开发大致分两个阶段,第一阶段:开发阶段,此时所打包都是使用 development 的证书,极光和友盟统计的账号都是使用开发者自己申请的账号,webservice 的地址使用开发环境地址;第二阶段:uat 阶段,此时属于预发版阶段,此时打包使用 ad-hoc 的证书,极光和友盟统计的账号使用公司申请生成账号,webservice 使用的特定的预发版环境;另外,打上传到 App Store 的生产包,使用 distribution 的证书,webservice 的地址使用生产环境的地址。

由此,可新建一种 build configuration, 由 Xcode 自动生成的 Release 复制而来,如下所示:

新建Configurations

并命名为 PreRelease。

官方文档Adding a Build Configuration 中如下提到:

A configuration file is a plain text file with a list of build setting definitions, one per line. You can base a build configuration only on a configuration file that is in your project, not on an external file.

When you base a target or project’s build configuration on a configuration file, that build configuration automatically inherits the build setting definitions in that configuration file (and any configuration files it includes). If you then modify the value of any of those build settings in the target or project, the new value is used instead of the value in the configuration file.

Build settings defined at the target level override any values assigned to those build settings at the project level. Therefore, target-level configurations take precedence over any project-level configurations.

这里需要注意的是:当你的 target-level 或者 project-levle 的 build configurations 基于配置文件时,build configuration 会自动继承配置文件(以及配置文件中引入的配置文件)中定义的 build settings,但是如果你又在之后 target 或者 project 中修改了配置文件中定义的 build settings 值,那么最终配置文件中的值会失效,实际使用的是 target 或者 project 中设置的值。

这里鉴于公司的情况,新建了 Debug.xcconfig/PreRelease.xcconfig/Release.xcconfig 配置对应于开发阶段、预发版阶段、上传 AppStore 三种情况下的打包。

新建一个 xcconfig 目录,在该目录下新建配置文件:

创建配置文件

根据项目情况,每个配置文件中都包含同样的 key 值,内容大致如下:

1
2
3
4
5
6
7
8
9
10
11
//网络请求baseurl
WEBSERVICE_URL = @"http:\/\/127.0.0.1"

//友盟配置
UMENG_APPKEY = @"xxxvvv555999=="

//极光推送配置
JPUSH_DEVELOPMENT_APPKEY = @"nnncccvvvwww"
IS_PRODUCATION = NO

#include "Generator.xcconfig"

你可在配置文件中包含其他配置文件,其中 Generator.xcconfig 文件的内容是:

1
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) WEBSERVICE_URL='$(WEBSERVICE_URL)' MESSAGE_SYSTEM_URL='$(MESSAGE_SYSTEM_URL)' UMENG_APPKEY='$(UMENG_APPKEY)' IS_PRODUCATION='$(IS_PRODUCATION)'

其作用是将配置文件中定义的常量定义成预编译宏,以便于在代码中获取。

其中 GCC_PREPROCESSOR_DEFINITIONS, 文档如下:

Space-separated list of option specifications. Specifies preprocessor macros in the form foo (for a simple #define) or foo=1 (for a value definition). This list is passed to the compiler through the gcc -D option when compiling precompiled headers and implementation files.

GCC_PREPROCESSOR_DEFINITIONS 是 GCC 预编译头参数,通常我们可以在 Project 文件下的 Build Settings 对预编译宏定义进行默认赋值。在 Xcode7 下的路径为 Build Settings->Apple LLVM 7.x Preprocessing->Preprocessor Macros,

GCC_PREPROCESSOR_DEFINITIONS

想必大家看这个宏的名字已经知道它的作用了, 使用上和在 pch 头文件中添加宏定义没有太大的区别, 但有以下好处:

  1. Xcode 的 Project 的 Build Settings 是由一个 plist 文件进行描述的, plist 本质上是一个 XML 配置文件, 通过外部的脚本比较容易去修改。
  2. Preprocessor Macros 可以按照 Configuration 选项进行默认配置, 也就是说可以根据不同的环境预先制定不同定义的宏,或者为不同环境下的相同变量定义不同的值

xcconfig 支持可以根据不同的 Configuration 选项配置不同的文件。不同的 xcconfig 可以指定不同的 Build Settings 里的属性值, 这样子我们就可以通过项目 xcconfig 去修改 GCC_PREPROCESSOR_DEFINITIONS 的值了(最终目的就达到了)。

配置文件中变量定义好之后,怎么让 Xcode 自动加载呢?如下图设置所示,是将 project-level 的 build settings 基于配置文件,三种情况的 configurations 分别选择与之对应的配置文件。

将配置文件与项目关联

当我们想把 project-level 或者 target-level 中的 Build Settings 的设置挪动到 xcconfig 配置文件来设置时,是否需要一个个手动输入呢?当然不是,直接在 Build Settings 中选中你想要在 xcconfig 中配置的键值对所在行(当然也可以选多行),command + c复制,然后到对应的 xcconfig 中去粘贴就好了,记得在 Build Settings 中改为你想要的值后再复制,如果为默认值的话则只可复制其键。如果需要改回去的话,还是选中这行,command + delete 就恢复默认值了。

现在我们将设置挪动到了配置文件中,所有的配置文件都是键值对类型的文本文件,但是当同一个键同时存在于 target-level、project-level 和配置文件中时,到底是哪一个键值对起作用了呢?现在看看下图。

多个配置项列

注意: Xcode以从左至右的顺序设置解析的优先级,从左至右优先级降低,最左边的具有最高优先级,即 target-level > project-level > 自定义配置文件 > iOS 默认配置;且最左列 Resolved 列显示的是最终使用的值。那么如何使 Xcode 使用配置文件中的配置项呢?这需要选中要使用配置文件的行,点击 Delete 按键,你会发现项目的默认设置已经被删除,且 xcconfig 的配置文件列被标记为绿色。标记为绿色代表该列的值生效,其值应该与 Resolved 列的值相同。

最后,你可以像如下示例使用 xcconfig 中定义的宏:

1
NSLog(@"webservice url: %@, umeng appkey: %@", WEBSERVICE_URL, UMENG_APPKEY);

通过以上步骤,就达到了使用 xcconfig 文件来配置开发不同阶段时的环境变量的目的了。

 

 

 

 

 

 

 

 

让我们来看看 XCConfig 文件如何才能在多个拥有不同配置的 target 中良好地工作。

今天我本计划学习一些新东西,因此我搜索了 mozilla/firefox-ios 库(译者:这是在火狐浏览器在 github 的一个开源项目)的相关信息,接着我发现他们会在项目中使用大量的配置文件。

我曾经在几个项目中使用过 XCConfig ,但是我并没有在现在开发的项目中使用它。因为这个项目有多个不同配置的 target,因此我开始思考如何才能有效且简单地管理这些 target 。

用例

这个项目现在已经被我的团队接手了。客户的团队先开发了大约半年的时间,最后决定将项目完全外包出去。这个项目一个麻烦的事就是 target 有不同的配置,因此如何更好地解决,是个棘手的问题。

项目由十个应用 target 组成,2个总的 target 做些业务,以及一个测试 target 。每一个 target 使用不同的尾部和不同的 “api keys”,以及其他像用于 hockeyapp(HockeyApp 是一个用来分发你的程序并收集应用的崩溃报告的收集框架,类似友盟) token 的键(key)。每一个 target 有自己的预处理宏,如:“TARGET_A”, “TARGET_B”等...(虚构的名字)。然后,token,api keys,后端的 url 被存储在 plist 文件中。因此很自然需要一些类来封装这个文件,并且有语法分析程序以及可以提供给我们适当的键。这个类有超过200行的代码,对我来说仅仅阅读这些数据就要花费很多时间。

因此,我想或许可以使用 XCConfig 文件来简化和替代,而不是使用语法分析程序和十个个预处理宏(一个 target)去决定从 plist 文件应该返回什么值。你可以在下面找到我的解决方案。可能不是最好的方案,但是此刻应该是最好的。如果你有更好的方案,我很愿意去拜读 :)

概述

核心思想是使用一些有层级的配置文件。第一层是用于存储最普通的数据,第二层用于区分 debug 和 release 模式,最后一层用于关联特殊 target 的设置。

xcconfig 文件配置文件 问题_第1张图片

Common.xcconfig

这个文件存储着类似应用名称,应用版本,bundle version,以及其他 debug和 release target 中通用的常见配置。


//
//  Common.xcconfig
//  
//

APP_NAME = App
APP_VERSION = 1.6 APP_BUNDLE_ID = 153 

考虑到为十个 target 改变相应的应用版本和 bundle 可能会消耗很多时间。其他的选项可能会创建聚合的 target ,这样可以在每次 Cmd+B的时候更新Info-plist 文件,但是我会避免这样的情况并且让项目不会比现在更复杂。

Common.debug 和 Common.release

这个文件能够存储可用于 debug 和 release target 的最常用配置。文件包含 Common.xcconfig 并且能够重写它的变量。例如:你可以通过重写一个变量,轻易地把每个 debug target 的应用名称改为 “App Debug” 。对于存储常见的用于开发和发行版本 target 的 API Key,这里也是很好的地方。

提示:使用通用配置文件和 CocoaPods

如果你使用 CocoaPods,你应该相应地在你的配置文件之一中包括(include)Pods.debug.xcconfig 或者 Pods.release.xcconfig。我推荐先在项目信息标签中设置你的配置文件然后执行 pod install 去让 Pod 项目重新配置。在安装之后,你应该及时地把 Pod 配置文件中的其中一个包括(include)到你自己的文件中去。


Error:
[!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configurations of the target TARGET_NAME to Pods/Target Support Files/Pods/Pods.debug.xcconfig or include the Pods/Target Support Files/Pods/Pods.debug.xcconfig in your build configuration. // // Common.debug.xcconfig //  // #include "Common.xcconfig" #include "Pods/Target Support Files/Pods/Pods.debug.xcconfig" APP_NAME = App Debug API_KEY_A = API_KEY_HERE API_KEY_B = API_KEY_HERE 

PerTarget.xcconfig

我确实不需要在这个层级使用 debug/release 配置文件(因为项目中的其他遗留问题),所以我只是用包括适当的 Common.debug.xcconfig 或者 Common.release.xcconfig 的 PerTarget.xcconfig 文件。但是最好应该有 debug 和 release 配置文件。在这个层级,你可以配置关联到特殊 target 的东西。

swif
//
//  Develop.xcconfig
//  
// #include "Common.debug.xcconfig" BACKEND_URL = http:\/\/develop.api.szulctomasz.com SOME_KEY_A = VALUE_HERE SOME_KEY_B = VALUE_HERE 

访问变量

所有的配置文件被存储了。现在是时候去使用他们了。像我例子中有这么多的 target,我可以把 Info.plist 文件的数量减少到只有1个,由于所有的不同的地方都已经在 xcconfig 文件中了,所以这一个文件可以替代多个文件。

你可以看到在你通过这些配置文件构建应用之后,有一些值出现在项目的 Build Setting 的 “User-Defined” 部分。

如果你想要使用配置文件中的变量,例如,在一个target的 Info.plist 文件中,你需要使用这种写法:$(VARIABLE)。使用这种方式,你可以设置 “Bundle Identifier” , “Bundle name” , “Bundle version” 以及其他你想要配置的事项。

在代码中访问其他变量看起来有点不一样,我发现最简单的方法就是在 Info.plist 中创建附加的区域,通过使用相同的变量名称和使用上述的写法去设置值。这样你就可以在你的代码中读到这些值。


if let dictionary = NSBundle.mainBundle().infoDictionary {
    let appName = dictionary["APP_NAME"] as! String let appVersion = dictionary["APP_VERSION"] as! String let appBuildVersion = dictionary["APP_BUILD_VERSION"] as! String print("\(appName) \(appVersion) (\(appBuildVersion))") let backend = (dictionary["BACKEND_URL"] as! String).stringByReplacingOccurrencesOfString("\\", withString: "") print("backend: \(backend)") } 

这里是 tomkowz/demo-xcconfig 的代码,从里面你可以看到一些使用 xcconfig 文件的例子。

总结

Xcode 配置文件给出了配置 target 的简易方式,并且支持方便地维护项目配置。在我用例中,可以很棒地切换到这些文件,因为现在维护项目配置和我没有使用这个解决方案之前比起来简单了很多。

 

参考链接:

 

1.http://liumh.com/2016/05/22/use-xcconfig-config-specific-variable/

2.https://segmentfault.com/a/1190000004080030

3.http://szulctomasz.com/xcode-xcconfig-files-for-managing-targets-configurations/

转载于:https://www.cnblogs.com/Jenaral/p/5661572.html

你可能感兴趣的:(xcode,cocoapods,后端)