利用Xcode进行开发时需要进行很多build setting的设置以便能让项目按照设置的进行编译,同时有时候需要在编译时利用script脚本进行一些设置,本文主要介绍xconfig文件和script脚本在Xcode开发中使用。

作者:MambaYong
链接:https://www.jianshu.com/p/81bbe0b0761a

Xcode编译

在使用xconfig时有几个关于Xcode的概念是需要理解的,这里我进行通俗简单的说明,同时需要知道Xcode在编译的过程中具体帮我们做了那几件事情。

Xcode target

在实际开发中一个Xcode创建的项目是可以有多个taget的,比如我们创建一个widget时Xcode会自动新建一个target对应这个widget,也可以自己新建,同一个项目有多个target可以满足不同的测试场景,比如在前期开发阶段使用一个target,到UAT阶段使用另外一个target。一个target对应一个product,也就是编译后安装到手机上的项目,target定义了生成的唯一 product, 它将构建该product 所需的文件和处理这些文件所需的指令集整合进 build system 中,这些指令以 build setting 和 build phases的形式存在,我们用xconfig文件来设置 build setting,同时将script脚本添加到build phases 中。

Xcode project

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

Xcode scheme

一个project可以有多个target,但是当前的target只能有一个,scheme就是用来确定当前的target的,并制定当前的target使用哪种configuration。

新建configuration

打开项目编辑栏选择上面的progect同时选择info栏,可以看到Xcode默认添加了二个Debug和Release的configuration,点击做下角的+号按钮选择复制Debug或者Release其中一个configuration来新建并命名一个自己想取的名字,我这里命名为Mamba。

Configuration文件的使用

平时手动的在Xcode中进行项目的一些build setting设置还是比较麻烦的,一个是需要在Xcode中进行搜索,另外一个是不好管理,例如需要在debug或者release下进行不同的设置的话就比较麻烦。利用Configuration文件来代替手动设置则更加的方便,直接新建Configuration Setting file类型文件,如下图所示:

利用Configuration设置不同的项目名

Configuration文件是可以继承的,一般先建立一个Common Configuration文件用来作为父类,为此新建一个名为Common的Configuration文件,并加入如下代码:

APP_NAME = TestDemo

然后分别新建名为debug,Mamba和release的Configuration文件,并加入如下代码:

  • debug
#include "Common.xcconfig"
APP_NAME = $(inherited)Debug
  • Mamba
#include "Common.xcconfig"
APP_NAME = $(inherited)Mamba
  • release
#include "Common.xcconfig"
APP_NAME = $(inherited)Release

上面利用#include进行导入依赖的Configuration文件,并利用$(inherited)来引用依赖的Configuration文件中的变量。

Configuration文件中的语法一般是SETTING_NAME = VALUE,具体等式二边设置的值可见苹果官网.

设置Configuration

点击PROJECT导航栏并选择Info会发现多了一个上文我们添加的名为Mamba的Configuration。

点击左边的小三角箭头展开每个Configuration后可以设置项目的project级别的Configuration File和target级别的Configuration File,当然也可以默认不设置。分别设置三个Configuration下的project级别的Configuration File为Base,target级别的Configuration File则为对应的Configuration File,如下图所示:

查看是否设置成功

点击TARGETS导航栏,选择Build Settings并选中All和Levels滑到最下面可看见APP_NAME的值设置如下:

这里需要解释一下几个设置的级别:

  • Resolved: 最后生效的值
  • Target: 显示在Target级别生效的值,Target级别的优先级是高于Project的,并且默认继承Project设置的值。
  • Project: 显示在Project级别生效的值,往常在Xcode
  • General设置的值就是这一级别的。
  • iOS Default : 显示iOS默认设置的值。

加上Configuration File后优先级顺序从低到高如下:

  • Platform defaults
  • Project.xcconfig file
  • Project file build settings
  • Target .xcconfig file
  • Target build settings

设置Info.plist

最后为了通过Configuration File来控制APP运行时名字的显示,需要在Info.plist中链接Bundle display name属性(没有的话需要新增)到我们上面设置的user-defined setting(APP_NAME) 上,为此修改Info.plist中Bundle display name的值为 $(APP_NAME)。

测试是否生效

在Scheme页面分别选择debug,release和mamba三中不同的Configuration环境运行APP成功的根据不同的Configtation设置不同的项目运行名字。

利用xconfig文件实现OC条件编译

在开发中经常需要进行条件编译,在OC中可以利用pch文件配合宏来实现,例如如下:

#ifdef DEBUG
#define BaseURL @"192.168.1.1:8080/appname/api"
#define PublicKEY @"QWE3R23WR09WURI220WR3TTY5ET3CR2X"
#else
#define BaseURL @"http://api.appname.com"
#define PublicKEY @"32GDG4575UB5M97O7M2X32RFH53QWT43"
#endif

通过在pch文件中利用条件编译定义不用的宏来实现项目的动态切换配置,上述宏定义一般定义在.pch中,通常.pch文件中定义的宏都比较杂乱,希望能单独放在一个独立的文件中,可以通过新建一个头文件env.h, 把上述宏定义放到env.h中,在需要使用的时候导入头文件即可,把环境参数单独放在一个独立的头文件中,更加简洁,职能更加专一,也便于维护但是这种做法还不是最好的,因为还需要手动导入头文件,而且生产环境参数和开发环境参数是放在同一个文件中而是不是独立分开的,要想独立分开并且使用时又不用导入头文件可以通过Xcode中的Configurations Setting Fil(.xcconfig)来解决,这应该是最优的实现方式。

xconfig文件的设置

在上面的Debug.xconfig和Mamba.xconfig文件中分别加入如下代码:

  • Debug.xconfig
WEBSERVICE_URL = @"www.baidu.com"
  • Mamba.xconfig
WEBSERVICE_URL = @"www.jd.com"

这样只是自定义了一个Build Setting变量,不能代码里像使用宏那样使用,Xcode是支持利用GCC_PREPROCESSOR_DEFINITIONS在定义宏的,在Common.xconfig文件中加入如下代码:

GCC_PREPROCESSOR_DEFINITIONS = $(inherited) WEBSERVICE_URL='$(WEBSERVICE_URL)'

在TARGET导航栏中Preprocessor Macros即可看见我们定义的宏。

代码使用

可以在代码中直接使用定义的宏,当切换Configuration时则会根据.xconfig文件输入不同的打印。

- (void)viewDidLoad {[super viewDidLoad];NSLog(@"-----------%@-------------",WEBSERVICE_URL);
}

使用#include语法来包含其他配置文件,如#include “Common.xcconfig”, 最好是放在文件的最后面,放在文件的开头也可以。Common.xconfig中第一个键的配置必须有:GCC_PREPROCESSOR_DEFINITIONS = (inherited),没有Xcode会报错,暴露自定义键时的语法:宏名=′(inherited),没有Xcode会报错,暴露自定义键时的语法:宏名='(inherited),没有Xcode会报错,暴露自定义键时的语法:宏名=′(key)‘,在代码或其他地方使用宏名来引用,’$(key)':通过key来指定每个模式下的对应的自定义键的名字,通常将宏的名字和key的名字保持一致, 注意 等号前后一定不能有空,Common.xconfig中第一个key是GCC_PREPROCESSOR_DEFINITIONS = $(inherited) 后面跟自定义的key,注意在第一个key后面跟上自己定义的key的时候一定不要回车换行,敲一个空格,然后在同一行后面追加就行了,换行会编译错误, 不能换行,不能换行,不能换行!

Swift中条件编译的实现

在Swift中是不支持通过GCC_PREPROCESSOR_DEFINITIONS来定义宏的,但是可以通过定义Custom Flags进行定义,这里介绍另外一种方法,还是通过.xconfig文件进行获取我们需要的宏。前面我们通过info.plist获取到了.xconfig文件中自定义的变量,再次我们同样通过info.plist来获取自定义的变量的值来当做宏使用,首先在info.plist中新建一个WEBSERVICE_URL变量,并设置值为’$(WEBSERVICE_URL)',由于需要解析info.plist中的变量,再次封装一个config.swift的类用来解析:

import Foundation
enum Config {static func stringValue(forKey key: String) -> String {guard let value = Bundle.main.object(forInfoDictionaryKey: key) as? String else {fatalError("Invalid value or undefined key")}return value}
}

代码使用

import UIKit
class ViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()print(Config.stringValue(forKey:"WEBSERVICE_URL"))}
}

相比较于OC版本的是不能直接定义宏,需要通过在info.plist定义后并通过方法取出值后才能使用,稍微麻烦了一点。

script的使用

上文我们已经知道xconfig文件的使用,其实在编译之前不只是变量的自定义或者获取项目的一些默认参数,还可以在获取这些参数的基础上,将这些参数作为script脚本的变量来做一些更有意义的事情,Xcode是支持在编译之前链接script脚本的。

script的初步认识

脚本一般来说就是可执行的二进制文件,下面先制作一个简单的脚本加深认知(实例代码采用Swift),首先新建一个名为HelloXcode.swift文件,加入如下代码:

import Foundation
@main
enum MyScript {static func main() {print("Hello Xcode")}
}

下面我们用终端来编译上面的HelloXcode.swift文件,cd到文件所在的目录执行以下代码:

xcrun --sdk macosx swiftc -parse-as-library HelloXcode.swift -o CompiledScript

利用macOS SDK来编译HelloXcode.swift并输出名为CompiledScript的二进制脚本文件,此时可以直接在当前目录利用./CompiledScript执行该脚本文件,会直接输出打印HelloXcode。为了在Xcode编译阶段就能运行脚本,我们需要将脚本插入到Xcode的Build Phases中,首先我们先新建一个Build Phases如下所示:

Xcode中的Build Phases选项卡是Xcode build项目的中心,Xcode在编译项目时其实帮我们做了如下几件事情:

  • 确定项目的一些依赖并编译
  • 编译项目的代码
  • 链接上面编译的依赖文件
  • 复制资源文件例如图片等到项目bundle中

这里我们是要在项目编译开始之前就运行脚本,所以需要调整新增加的Build Phases的顺序,直接拖到Denpencies下面,如下图所示:

点击刚刚新加的Build Phases可以重命名,展开后加入如下代码:

下面的Input Files可以理解为脚本的变量,这里将HelloXcode.swift相对工程文件所在的路SCRIPTINPUTFILE0进行引用,SCRIPT_INPUT_FILE_0进行引用,SCRIPTI​NPUTF​ILE0​进行引用,(SRCROOT)代表工程文件所在的目录,运行项目在build log(不是打印台)会看见如下输出:

script的实际运用

利用script来实现每当build的时候改变 Info.plist中Bundle version或者Bundle version string (short)的值,新建一个IncBuildNumber.swift 文件,加入如下代码:

import Foundation@main
enum IncBuildNumber {static func main() {guard let infoFile = ProcessInfo.processInfo.environment["INFOPLIST_FILE"]else {return}guard let projectDir = ProcessInfo.processInfo.environment["SRCROOT"] else {return}if var dict = NSDictionary(contentsOfFile:projectDir + "/" + infoFile) as? [String: Any] {guard let currentVersionString = dict["CFBundleShortVersionString"]as? String,let currentBuildNumberString = dict["CFBundleVersion"] as? String,let currentBuildNumber = Int(currentBuildNumberString)else {return}dict["CFBundleVersion"] = "\(currentBuildNumber + 1)"if ProcessInfo.processInfo.environment["CONFIGURATION"] == "Release" {var versionComponents = currentVersionString.components(separatedBy: ".")let lastComponent = (Int(versionComponents.last ?? "1") ?? 1)versionComponents[versionComponents.endIndex - 1] = "\(lastComponent + 1)"dict["CFBundleShortVersionString"] = versionComponents.joined(separator: ".")}(dict as NSDictionary).write(toFile: projectDir + "/" + infoFile, atomically: true)}}
}

当Xcode在执行run script phase时会通过环境变量environment variables来共享build settings,可以将环境变量在这里理解为全局变量,这里通过环境变量拿到了info.plist中的CFBundleShortVersionString和CFBundleVersion变量,并根据CONFIGURATION配置的是Release还是Debug来修改对应的BundleVersion,至此每当build时都会改变相应的BuildVersion值。

总结

本文主要介绍了利用xconfig文件如何进行项目的动态配置,并进行了实际的演示,同时介绍了script在Xcode中编译的基本使用,并配合xconfig文件能让Xcode在编译前做更多有意义的事情。

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。

针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

需要资料的朋友可以扫描二维码,免费领取资料

【Android】iOS开发中xconfig和script脚本的使用相关推荐

  1. ios 开发 键盘 android,UiOS开发中ITextView回收或关闭键盘使用方法总结

    iOS开发中,发现UITextView没有像UITextField中textFieldShouldReturn:这样的方法,那么要实现UITextView关闭键盘,就必须使用其他的方法,下面是可以使用 ...

  2. ios 开发中 动态库 与静态库的区别

    使用静态库的好处 1,模块化,分工合作 2,避免少量改动经常导致大量的重复编译连接 3,也可以重用,注意不是共享使用 动态库使用有如下好处: 1使用动态库,可以将最终可执行文件体积缩小 2使用动态库, ...

  3. iOS开发中使用UILabel设置字体的相关技巧小结

    这篇文章主要介绍了iOS开发中UILabel设置字体的相关技巧小结,代码基于传统的Objective-C,需要的朋友可以参考下 一.初始化 复制代码代码如下: UILabel *myLabel = [ ...

  4. iOS 开发中的多线程

    线程.进程 什么是线程.进程   有的人说进程就像是人的脑袋,线程就是脑袋上的头发~~.其实这么比方不算错,但是更简单的来说,用迅雷下载文件,迅雷这个程序就是一个进程,下载的文件就是一个线程,同时下载 ...

  5. iOS开发中使用[[UIApplication sharedApplication] openURL:]加载其它应用

    iOS 应用程序之间(1) 在iOS开发中,经常需要调用其它App,如拨打电话.发送邮件等.UIApplication:openURL:方法是实现这一目的的最简单方法,该方法一般通过提供的url参数的 ...

  6. iOS开发中各种关键字的区别

    1.一些概念 1.浅Copy:指针的复制,只是多了一个指向这块内存的指针,共用一块内存. 深Copy:内存的复制,两块内存是完全不同的, 也就是两个对象指针分别指向不同的内存,互不干涉. 2.atom ...

  7. Xamarin iOS开发中的编辑、连接、运行

    Xamarin iOS开发中的编辑.连接.运行 创建好工程后,就可以单击Xamarin Studio上方的运行按钮,如图1.37所示,对HelloWorld项目进行编辑.连接以及运行了.运行效果如图1 ...

  8. ios开发中计算代码运算时间_iOS日历、日期、时间的计算

    时间和日历的计算在iOS开发中经常看到,经常看到大家在百度,开源中国等搜索这些答案.今天写个简单的时间和日历有关的计算. 获取一个月的总天数 1.获取当月的天数 - (NSInteger)getNum ...

  9. IOS开发中UIBarButtonItem上按钮切换或隐藏实现案例

    IOS开发中UIBarButtonItem上按钮切换或隐藏案例实现案例是本文要介绍的内容,这个代码例子的背景是:导航条右侧有个 edit button,左侧是 back button 和 add bu ...

最新文章

  1. 机器学习必读TOP 100论文清单:高引用、分类全、覆盖面广丨GitHub 21.4k星
  2. 荣品瑞星微RK3399-RP3399开发板
  3. ESX虚拟机文件列表详解
  4. 打印一个字符串的所有排列。
  5. Matlab实现线性回归和逻辑回归: Linear Regression Logistic Regression
  6. 三方登录(微博为例)
  7. mfc中嵌入python_Python 中的 Hook 钩子函数
  8. SwiftUI之深入解析@StateObject、@ObservedObject和@EnvironmentObject的联系和区别
  9. 进程间通信————信号
  10. activity 点击后传递数据给fragment_Fragment 新特性 : Fragment Result API 使用以及源码分析
  11. 【POJ1083】 Moving Tables (并行的搬运)
  12. Docker容器中常见的十种误区
  13. SpingMVC简易学习笔记二(springMVC的请求和响应)
  14. Ubuntu下局域网内+花生壳远程调试程序
  15. Review Board的几点使用体会
  16. R语言和医学统计学(11):球形检验
  17. 《学习的本质》读后感
  18. win10永久关闭自动更新方法
  19. 手机如何制作gif?简单三步在线合成gif动图
  20. 2021 新款 Macbook Pro 轻松隐藏刘海

热门文章

  1. ZwSe2团队共识V0.1
  2. [插件使用] SwitchHosts自动更新Github Hosts文件
  3. ZYNQ学习之旅--PS_AXI_VDMA(利用VDMA实现将PS端的数据显示在PL端的HDMI上)
  4. 信息安全文章搜索引擎技术原理
  5. GDR(Gradual Decoder Refresh)帧
  6. 【hdu3709】平衡数
  7. 服装管理系统c语言(大一)
  8. Rime常用配置||输入默认英文
  9. ML_12 Sum-Produkt Networks 和积网络
  10. 生信工作流框架搭建 | 02-nextflow 实战