iOS多环境配置之Configurations + xcconfig

前言

一个APP的开发涉及到多种环境在所难免,比如Test环境,UAT(User Acceptance Test)用户验收测试环境,Release环境等等。我的项目需求是,在不同的环境下在代码中使用全局性常量值不同,比如说请求所用的IP。这里只是作一个简单的记录和分享。

Configuration配置

配置Configuration的目的是为了增加或删除编译环境,对于不同编译环境下的参数需求可以到xcconfig中进行设置。

1、进入工程的info页,下面红框中标明的是工程创建时自带的两个编译环境Debug和Release。

1.png

点击下面的加号添加新的编译环境,根据需要选择Duplicate Debug和Duplicate Release,他们的区别在于,后者会进行各种优化,包括编译的优化,导致不能调试(不能通过断点、LLDB查看变量),另外Release环境下打的包也会更轻量一些。这里我将Debug环境名改为UAT_α,并增加一个基于Debug 的UAT_β环境。


2.png

插曲:如果想要在Relase环境下去调试可以前往Bulid Setting总进行设置,将Release下的参数换成和Debug一样即可:


3.png

2、配置预编译宏,这就相当于在pch文件当中去定义宏,可以在项目中做不同环境的区分,进而进行操作。进入工程的Build Setting ->Preprocessor Macros:

Snip20170629_10.png

我们看到UAT_α和UAT_β对应的值均为DEBUG = 1,我们可以理解的是在两种环境下同时定义了#define DEBUG 1,为了表示区别可以分别设置为不同的宏名和值,例如:ConfigDemo_ UAT_α=0,ConfigDemo_ UAT_β=1(等号两边不能留空格,否则会编译出错)。GCC提供了一种动态编译的方法:ConfigDemo_{CONFIGURATION}=1,这两种设置方法是等价的(注意不要动$(inherited),他表示继承工程的默认设置)。

3、进行Scheme的配置,可以理解一个Scheme对应一个Target、一种环境(当然这里只有一个Target,只是编译环境不同)。 当我们切换不同的Scheme运行时就是切换不同的环境了。在Xcode的左上角选择添加新的Scheme,并分别添加ConfigDemo_ UAT_α,ConfigDemo_ UAT_β两个Scheme:


Snip20170629_11.png
Snip20170629_13.png

添加完Scheme后对其进行配置,我们刚才配置环境后总共有Release、UAT_α、UAT_β三种环境,在这里使他们分别对应ConfigDemo、ConfigDemo_ UAT_α,ConfigDemo_ UAT_β,选择Edit Scheme,这里可以将Run、Test、Archive改为对应的Scheme,关于他们的解释

Snip20170629_15.png

于是在代码中就可以进行如下操作:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
#ifdef ConfigDemo_UAT_α
    
#define TestString @"ConfigDemo_UAT_α"
    
#elif ConfigDemo_UAT_β
    
#define TestString @"ConfigDemo_UAT_β"
    
#else //Release
    
#define TestString @"Release"
    
#endif
    
    NSLog(@"TestString:%@",TestString);
    
    // 打印结果分别为Release、ConfigDemo_UAT_α、ConfigDemo_UAT_β
}

根据不同的Scheme配置不同的displayName、APPICON等可以参见文章最后的链接;

配置xcconfig

关于项目的参数需要做一下说明:

xcconfig文件的修改实际上是修改build setting中的参数。Projects 会包含一个或者多个 targets,这里可以理解每一种编译环境对应一个target,每一个 target 将会产出一个 product.这些指令以 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 生效,虽然这里并没有配置多个Target,但是这里还是可以理解一个Scheme对应一个Target。
另外要说明一点,对于项目中其他地方的配置,可以直接在操作板中复制粘贴到文件中来进行修改,最终xcconfig的设置会覆盖原来的设置。

对于需要单独配置参数的环境则生成一个xcconfig文件,没有单独配置xcconfig的则使用默认的设置,另外在这里需要生成一个公用的xcconfig文件。公共文件用于定义好统一的变量(宏),每一个环境去设置具体的值,形成多环境配置。
Snip20170629_8.png
Snip20170629_9.png
每个文件代码的写法:
  1. Common. xcconfig的写法
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) VariableA='$(VariableA)' VariableB='$(VariableB)'

解析如下:
其作用是将配置文件中定义的常量定义成预编译宏,以便于在代码中获取。
GCC_PREPROCESSOR_DEFINITIONS 是 GCC 预编译头参数,对应的设置在 Xcode8下的路径为 Build Settings->Apple LLVM 7.x Preprocessing->Preprocessor Macros,就是我上面提到的设置预编译宏,这里权且可以理解上面这一串代码是在Build Setting中设置预编译宏的代码化,而这里的设置并不会覆盖之前在Build Setting中的设置,只会将这些新增的设置添加到最后面。 上面代码的意思是,定义两个预编译的宏名字分别为VariableA、VariableB,他们具体的值就可以在不同的xcconfig中去设置,不同的xcconfig需要导入这一个Common. xcconfig的文件,这就实现了不同环境下动态的配置,不同环境下的预编译宏名一样只是值会不一样,而且这些xcconfig文件可以在不同的项目中使用。
这样就可以简单的理解为Common. xcconfig实现了一个Build Setting里面的一个方法,而其他的xcconfig文件调用这个方法进行具体设置。
上面在Preprocessor Macros中配置的用于在代码中区分不同环境的预编译宏,也可以直接在Common. xcconfig中设置,只是在代码中进行区分就要区分不同宏的值了。

2.其他xcconfig代码:

#include "Common.xcconfig"

VariableA = @"UAT_α.xcconfig_VariableA"
VariableB = @"UAT_α.xcconfig_VariableB"

3、将xcconfig与环境进行关联:
我们来到工程下的info->Configuration,


Snip20170630_19.png

我们发现,每一种环境下都有两个这样的设置,一个设置是工程的xcconfig文件配置,另一个则是target的xcconfig文件配置,在这里配置xcconfig意味着APP在运行的时候会去加载相应xcconfig中的设置。上面我们说到了配置的继承关系,那么这里只设置target的xcconfig就够了,而且你设置了工程的xcconfig就相当于在编译的时候将这些xcconfig的配置加载设置了两遍!其实你也会发现此时再设置工程的xcconfig也没有关系。这里我暂且只设置UAT_α和UAT_β环境下的xcconfig,Release环境留着调式pod。

2019-6-7更新:
经过上面的设置,在某些Xcode上会出现“use undecalred indentifire”的错误,经过不断尝试后发现,将工程的xcconfig文件对应为Commn就可以了,猜测与Xcode自身有关,配置文件的加载顺序会影响到代码的编译。

Snip20170630_20.png

接下来你就可以直接在代码中使用VariableA、VariableB宏了:

// 这里并不能在Release环境下使用这些宏,否则会报错。
   NSLog(@"%@ - %@",VariableA, VariableB);
    // ConfigDemo_UAT_α、ConfigDemo_UAT_β环境打印结果分别为
    // UAT_α.xcconfig_VariableA - UAT_α.xcconfig_VariableB
    // UAT_β.xcconfig_VariableA - UAT_β.xcconfig_VariableB

Pod调试,Pod安装SDWebImage

在删除上面的log代码之后,你会发现只有在Release环境下能够运行!首先我们来看看报错:

Snip20170630_22.png

是的,工程找不到相关文件了,其实这个你在终端的提示中就可以发现端倪:

Snip20170630_21.png

大概意思是你需要你配置的xcconfig文件中导入pod配置文件的路径。

在完成一次pod install之后,Pods工程会为每一个环境生成一个xcconfig文件,包括默认的debug环境,如果我们的目标工程ConfigDemo对应的环境没有配置xcconfig文件,那么就会在ConfigDemo工程下拷贝一份对应的xcconfig文件并自动在环境中完成配置,这种配置让工程能够使用pod。大家可以看到这里在ConfigDemo工程下只有Release. xcconfig文件的拷贝,而此时Release环境下的Configuration配置已经发生了改变。

Snip20170630_24.png
Snip20170630_25.png

我们只需要在其他xcconfig文件中导入相关的pod下的xcconfig就好了,记得使用相对路径,否则在其他电脑上就不能运行了:

#include "Pods/Target Support Files/Pods-ConfigDemo/Pods-ConfigDemo.release.xcconfig"

这个时候你就可以删除在ConfigDemo工程下的.release.xcconfig文件,并去Configuration中配置你自己的xcconfig文件了,记得重新pod install一下。

Demo:https://github.com/Randy1993/ConfigDemo

宏DEBUG丢失的问题

上述的配置结束之后,会导致Pod工程下UAT_α等缺少DEBUG宏,导致某些三方库无法正常使用,比如说MLeaksFinder无效。在Podfile中添加如下代码即可:

pod 'MLeaksFinder'
    
    post_install do |installer_representation|
        installer_representation.pods_project.targets.each do |target|
            if target.name == 'MLeaksFinder'
                target.build_configurations.each do |config|
                    if config.name == 'UAT_α'
                        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)','DEBUG=1']
                    end
                end
            end
        end
    end

参考文章:

http://www.jianshu.com/p/83b6e781eb51
http://liumh.com/2016/05/22/use-xcconfig-config-specific-variable/#xcconfig-environment 关于xcconfig文件的说明。
http://www.jianshu.com/p/51a2bbe877aa Configuration配置

Stay hungry,Stay foolish!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容