实际使用场景:
- debug模式和release模式的一些配置不一样,比如开发环境访问的后台服务器和线上版本访问的服务器地址不一样。
- 发布开发版程序AppIcon不同,包含抢鲜版或者体验版的特有图标
通常可以创建一个变量,保存开发版服务器地址,然后再复制创建同一个变量名保存线上版服务器地址。开发或者发布的时候根据需要,将需要的打开,不需要的注释。这样来回切换,达到匹配相应服务器的结果。这种方式虽然可以解决问题,但是不够优雅,稍微一个不留神,就会出错,如果不同的环境有多个不同的参数,那更得小心翼翼,提心吊胆的感觉是真难受。
那么如何优雅的解决这个问题?
这里提供3种方式:
- 通过多target的方式
- 通过配置变量configurations,Scheme环境变量的配置达到多环境的目的
- xcconfig文件,通过xcconfig的形式将对应的配置写到相应的文件里面,通过文件管理,更清晰一些,改动的幅度更小一些,同时配合git,将一些文件隐藏起来
随便打开一个项目代码,可能打开的是一个project或者一个workspace。那么project在我们开发中,在我们xcode中到底是什么东西?在Scheme中选择环境debug或者release又是代表什么意思呢?
先介绍一下xcode几个常见的名词
- Project:包含了项目所有的代码,资源文件,所有信息。
- Target:对指定代码和资源文件的具体构建方式。
- Scheme:对指定Target的环境配置。
- xcconfig:将配置写到对应的文件中
- workSpace:是多个project的合集。
所有的开发都是面向project,它是一个大的集合,包含了所有内容。Target指定某些代码来如何生成IPA包,Scheme作为一个监控,通过它来配置你编译时候的环境变量。Scheme中可以选择决定target中是debug起作用还是release起作用。
第一部分 通过多Target实现多环境
复制出的.plist文件是绝对路径。而且包含copy,这显然要做修改。
对于有强迫症的我,还做了两步:将plist文件的路径改为相对路径。修改Scheme的名称,带copy字样太难看了。
准备工作就绪。通过选择不同的Scheme,运行不同的target,在手机上会生成两个App,而且displayName和AppIcon已经变了。目前是两个target操作同一份代码。如果想在Dev模式下执行不同的代码,应该怎么做?可以通过宏定义,预先定义宏。
OC中可以在 target 的 Preprocessor Macros 里设置。preprocessor macros的意思就是 预处理器宏。
OC下的多环境配置通过多Target的方式处理完毕。
如果是Swift项目呢,或者是OC和Swift的混编项目要怎么通过多Target的方式处理呢?
在刚才项目中新建一个swift文件,让xcode给我们自动创建一个桥接器。新建两个Swift的UIViewController,分别命名为SecondViewController和ThirdViewController,现在想让FirstDemo的viewdidload进入SecondViewController,FirstDemo-Dev的viewdidload进入ThirdViewController。
检查Product Module Name是否为工程名FirstDemo-Dev
到这里就可以实现,运行不同的target进入到不同的ViewController。
选中 target --> Build Settings --> 搜索 Swift Compiler - Custom Flags
展开 Active Compilation Conditions 进行设置,只能写名称:TEST, 不能像OC一样设置TEST=1
展开 Other Swift Flags 同样可以设置 TEST,需要这样写:-D TEST,填完后是 -D 和 TEST 分成两行的,(如果项目中用到了 CocoaPods 可以参考它也是这样的)
-D的含义:就是将声明的变量设置为TRUE。
如果是 OC 和 Swift 混编的项目,OC也需要用到,则还是在 Preprocessor Macros 里添加一遍。也就是说OC中的预定义宏和Swift中是完全分开,无法共用的。
总结:1.步骤太多,操作繁琐2.存在多个plist文件,预定义宏变量相互对应关系复杂,稍有不慎,便会弄错
第二部分 通过Scheme实现多环境
Scheme是什么?就是控制环境变量,让项目中哪个变量起作用。常见的有debug和release。
Target就是往编译的时候添加参数,指明编译的方式,告诉编译器代码怎么编译成相应的结果。并不是控制或者改变一些什么东西。
在Scheme中切换到哪一种,就会使用Build Settings中对应的相关设置。Scheme的设置是全局的Configuration的设置。
有没有一种方式不用去手动切换Scheme中的3个Beta,Debug和Release?
这样有什么好处?针对先前开发环境和发布环境不同的服务器地址。我们可以有新的办法。
Host_URL变量的根据需要已经设置成了我们想要的状态,那么如何去读取到这个值?我们可以在info.plist中,设置一个变量进行读取,这样来达到暴露出来供程序使用的目的。
NSString *path = [NSBundle.mainBundle pathForResource:@"Info" ofType:@"plist"];
NSDictionary *infoDic = [[NSDictionary alloc] initWithContentsOfFile:path];
NSLog(@"%@",infoDic[@"HOST_URL"]);
这样就达到了多环境的目的。
总结:比上面多target方式要好,不用创建多target,只需要在一个Build Setting里面就能配置。有多少个环境,就可以创建多少个Configuration。但还是有个弊端,还是必须要在Build Setting中进行相关的配置。
我们正常的一个项目,通过Cocoapods进行引用的时候,对应的我们的target,Build Setting中Other Link Flags,Path to Link Map File,Framework Search Paths等等都是有参数的,如果使用Scheme这种方式,我们还是要在Build Setting中不停的配置。
那么能否通过一个文件的方式,来对整个Build Setting中我们想做的修改,做统一的管理呢?放在一起,放在一个文件。
第三部分 xcconfig文件管理Build Setting多环境配置
Cocoapods中使用的就是xcconfig文件,当我们通过Cocoapods导入一个项目的时候,Cocoapods通常会给我们生成两个文件。一个为“项目名.debug.xcconfig”,一个为“项目名.release.xcconfig”。实际上是根据Configuration中的配置生成的。
xcconfig的本质是一个Key-Value形式。
创建我们自己的XCConfig文件
注意:以保存的目录作为开头XCConfig,-拼上APP的名称FirstDemo,拼上环境debug。
准备工作完成,如何实现多环境配置呢?
在xcconfig中开始写我们需要做区分的内容。比如添加一个端口号,在debug.xcconfig中加入PORT = 8080,在release.xcconfig中假如PORT = 5001。
一样需要在info.plist中暴露出来。
编译过后,再观察Build Setting。
xcconfig写入的规则是什么?与Build Setting中什么字段相对应。可以在苹果官方提供的资料里面查。Xcode Build Settings
总结:完美的实现在xcconfig文件中对Build Setting中参数的设置。
额外部分
1.在自己创建的xcconfig中引入pod生成的xcconfig文件:
#include "Pods/Target Support Files/Pods-FirstDemo/Pods-FirstDemo.debug.xcconfig"
啊哈,在这里运行的时候出现一个错误:
dyld: Library not loaded: @rpath/AFNetworking.framework/AFNetworking
Referenced from: /private/var/containers/Bundle/Application/F4E7F2A4-56BA-4E73-AA37-8349B2791333/FirstDemo-Dev.app/FirstDemo-Dev
Reason: image not found
dyld: launch, loading dependent libraries
DYLD_LIBRARY_PATH=/usr/lib/system/introspection
DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib
这里先不理会,但是证明了一个事情,那就是加载pod的xcconfig是起效的。
2.xcconfig文件冲突解决
自己创建的xcconfig文件中存在pod生成的xcconfig文件中相同的字段,那么就会覆盖pod中的字段,因此要想两个文件中的字段都生效需要使用继承标识:
$(inherited)
3.URL变量中存在//,后面的字符串会被当做注释
// 通过定义一个变量来解决
SLASH =/
HOST_URL = http:${SLASH}/192.168.1.100
4.条件设置
OTHER_LDFLAGS[config=Debug][sdk=iphonesimulator*][arch=x86_64] = $(inherited) -framework "AFNetworking"
经过上述条件设置后AFNetworking库仅会在Debug、模拟器、X86_64架构下编译
[sdk=iphoneos*]
表示真机