偶然看到一个很有趣的问题:如何在ios环境下实现多个环境同时打包。
谈到多环境,我想大多公司都至少有2-3个环境,比如Test环境,UAT(User Acceptance Test)用户验收测试环境,Release环境等等。当需要开发打多个包的时候,一般常见做法就是直接代码里面修改环境变量,改完之后Archive一下就打包了。或者在App中内置一个切换的按钮,实现环境的切换。
或者,你们公司已经搭建了Jenkins环境,利用Jenkins环境就可以给app来配置一下多个环境变量,之后Jenkins分别再不同环境下自动集成即可。
那么如何实现ios的多环境打包呢?参照网上的一些方案这里做一个简单的总结。要实现多环境打包,现在主流的方案有三种(各有优劣)。 1.利用Build Configuration来配置多环境 2.利用xcconfig文件来配置多环境 3.利用Targets来配置多环境
做过ios开发的人都知道Build Configuration,系统默认提供2个环境,一个Debug,一个Release。这里,我们利用的就是Build Configuration给我们提供的配置功能来实现多环境打包。具体步骤如下:
1,新建Build Configuration 点击Project里面找到Configuration,然后选择添加一个Configuration,系统默认是2个,一个Debug,一个Release。这里我们需要选择是复制一个Debug还是Release。Release是不能调试程序,因为默认是屏蔽了可调试的一些参数。
这里我们新增一套环境:Configuration。在这一套包含了一些编译参数的配置集合。如果此时项目里面有cocopods的话,打开Configuration Set就会发现是如下的样子。
然后打开项目的pod文件,打开配置是会看到如下信息:
说明:pod安装完成之后会自动生成xcconfig文件,如果你手动新建这个xcconfig,然后把原来的debug和release对应的pod xcconfig文件内容复制进来,这样做是无效的,需要pod自己去生成xcconfig文件才能被识别到。
新建完Build Configuration,这个时候需要新建pod里面对应的Build Configuration,要不然一会编译会报错。如果没用pod,可以忽略一下这一段。
2,新建Scheme
新建完成之后,我们就可以编辑刚刚新建的Scheme,我们可以把Run模式和Archive都改成新建Scheme。
注意:如果是使用了Git这些协同工具的同学这里还需要把刚刚新建的Scheme共享出去,否则其他人看不到这个Scheme。
3,新建User-defined Build Settings 回到Project的Build Settings里面来,Add User-Defined Setting。
CustomAppBundleld是为了之后打包可以分开打成多个包,这里需要3个不同的Id,建议是直接在原来的Bundleld加上Scheme的名字即可。CustomProductName为app显示在手机上的名字,建议直接按环境给予描述,例如:测试(debug),线上(relase),UAT等。
需要注意的是:Pods的Build_DIR这些目录其实是Pods自己生成好的,之前执行过Pod install 之后,这里默认都是配置好的,不需要再改动了。
4,修改info.plist文件 和 Images.xcassets 打开info.plist文件。由于我们新添加了2个CustomAppBundleld 和 CustomProductName,这里我们需要把info.plist里面的Bundle display name修改成我们自定义的这个字典。编译过程中,编译器会根据我们设置好的Scheme去自己选择Debug,Release,TestRelease分别对应的ProductName。
为了方便区分不同的环境,你还可以对不同环境下App Icon,名字等做一个修改。
既然我们已经新建了这几个scheme,那接下来怎么把他们都打包成app呢?
这里分享下实际打包过程中的一些经验。
每个环境都要设置好Debug 和 Release!!!
千万别认为线上的版本只设置Release就好,哪天需要调试线上版本,没有设置Debug就无从下手了。也千万别认为测试环境的版本只要设置Debug就好,万一哪天要发布一个测试环境需要发Release包,那又无从下手了。我的建议就是每个环境都配置Debug 和 Release。
在打包的时候,一定要注意将Scheme的名字和编译方式区分开。选择一个Scheme,只是相当于选择了一个环境,并不是代表这Debug还是Release。
配置好上述之后,就可以选择不同环境运行app了。可以在手机上生成不同的环境的app,可以同时安装。
接下来说几种动态配置环境变量的方法。
进入到Build Settings里面,可以找到Apple LLVM Preprocessing,找到Preprocessor Macros,在这里我们是可以加一些环境变量的宏定义来标识符。Preprocessor Macros可以根据不同的环境预先制定不同定义的宏。
首先,新建3个名字一样的plist作为3个环境的配置文件(新建三个配置文件,分别放在3个不同文件夹下面即可)。
接下来我们要做的是在编译的时候(运行app前),动态的copy Configuration.plist到app里面,这里需要设置一个copy脚本。
进入到我们的Target里面,找到Build Phases,我们新建一个New Copy Files Phase,并且重命名为Copy Configuration Files。其中,脚本能保证我们的Configuration.plist 文件可以在编译的时候,选择其中一个打包进入app。再通过读取这个plist里面的信息就可以做到动态化。
当然使用一个单例也可以做到环境切换。新建一个单例,然后可以在设置菜单里面加入一个列表,里面列出所有的环境,然后用户选择以后,单例就初始化用户所选的环境。
提到xcconfig,就要先说说几个概念。Xcode Workspace、Xcode Scheme、Xcode Project、Xcode Target、Build Settings 。
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).
project就是一个个的仓库,里面会包含属于这个项目的所有文件,资源,以及生成一个或者多个软件产品的信息。每一个project会包含一个或者多个 targets,而每一个 target 告诉我们如何生产 products。project 会为所有 targets 定义了默认的 build settings,每一个 target 也能自定义自己的 build settings,且 target 的 build settings 会重写 project 的 build settings。
Xcode Project 文件会包含以下信息,对资源文件的引用(源码.h和.m文件,frame,资源文件plist,bundle文件等,图片文件image.xcassets还有Interface Builder(nib),storyboard文件)、文件结构导航中用来组织源文件的组、Project-level build configurations(Debug\Release)、Targets、可执行环境,该环境用于调试或者测试程序。
Xcode 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。需要注意的是, Project 可以包含多个 target, 但是在同一时刻,只会有一个 target 生效,可用 Xcode 的 scheme 来指定是哪一个 target 生效。
Build Settings
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.
build setting 中包含了 product 生成过程中所需的参数信息。project的build settings会对于整个project 中的所有targets生效,而target的build settings是重写了Project的build settings,重写的配置以target为准。
一个 build configaration 指定了一套 build settings 用于生成某一 target 的 product,例如Debug和Release就属于build configaration。
Xcode Scheme
An Xcode scheme defines a collection of targets to build, a configuration to use when building, and a collection of tests to execute.
一个Scheme就包含了一套targets(这些targets之间可能有依赖关系),一个configuration,一套待执行的tests。
1,新建一个xcconfig文件,然后在project设置一下。
接下来,把配置文件换成我们刚刚新建的文件。细心的读者可能发现,其实我们一直使用的cocopods就是用这个文件来配置编译参数的。
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 对预编译宏定义进行默认赋值。
在Build Settings里面的 Apple LLVM 7.X - Preprocessing - Preprocessor Macros 。
Preprocessor Macros 其实是按照 Configuration 选项进行默认配置的, 它是可以根据不同的环境预先制定不同定义的宏,或者为不同环境下的相同变量定义不同的值。
在xcconfig 我们可以写入不同的 Configuration 选项配置不同的文件。每一个 xcconfig 可以配置 Build Settings 里的属性值, 其实实质就是通过 xcconfig 去修改 GCC_PREPROCESSOR_DEFINITIONS 的值,这样我们就可以做到动态配置环境的需求了。
现在本地有这么多配置,到底哪一个最终生效呢?打开Build 里面的level,优先级是从左往右,依次降低的。Resolved = target-level > project-level > 自定义配置文件 > iOS 默认配置。
除了上面的功能以为,还能利用xcconfig动态配置Build Settings里面的很多参数。这其实类似于cocopods的做法。详情资料请大家自行搜索。
其实使用Scheme和xcconfig就可以实现多环境的功能,使用Targets反而显得更加麻烦。利用Targets可以瞬间大批量产生大量的app。相关使用可以查看:使用多Target来构建大量相似App
仅仅只用一套代码,就可以生产出7个app。7个app的证书都是不同的,配置也都不同,但是代码只需要维护一套代码,就可以完成维护7个app的目标。
下面来看一下如何使用Targets来生成不同的app。
一种方法是完全新建一个Targets,另外一种方法是复制原有的Targets。 其实第一种方法建立出Targets,之后看你需求是怎么样的。如果也想是做OEM这种,可以把新建出来的project删掉,本地还是维护一套代码,然后在新建的Targets 的Build Phases里面去把本地现有代码加上,参数自己可以随意配置。这样也是一套代码维护多个app。
第二种方法就是复制一个原有的Targets,这种做法只用自己去改参数就可以了。 再来说说Targets的参数。 由于我们新建了Targets,相当于新建了一个app了。所以里面的所有的文件全部都可以更改。包括info.plist,源码引用,Build Settings……所有参数都可以改,这样就不仅仅局限于修改Scheme和xcconfig,所以之前说仅仅配置一个多环境用Targets有点兴师动众,但是它确实能完成目的。根据第二章里面我们也提到了,Targets相当于流水线,仅次于Project的地位,可以想象,有了Targets,我们没有什么不能修改的。
关于Android如何批量打包和多环境打包请参考:Android多环境、多渠道打包