iOS开发xconfig和script脚本使用详解

 更新时间:2022年09月08日 16:24:40   作者:MambaYong  
这篇文章主要为大家介绍了iOS开发xconfig和script脚本使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

引言

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

Xcode编译

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

Xcode target

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

新建target

Xcode project

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

Xcode scheme

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

新建configuration

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

Configuration文件的使用

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

利用Configuration设置不同的项目名

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

APP_NAME = TestDemo

然后分别新建名为debugMambareleaseConfiguration文件,并加入如下代码:

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

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

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

设置Configuration

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

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

查看是否设置成功

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

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

  • Resolved: 最后生效的值
  • Target: 显示在Target级别生效的值,Target级别的优先级是高于Project的,并且默认继承Project设置的值。
  • Project: 显示在Project级别生效的值,往常在XcodeGeneral设置的值就是这一级别的。
  • 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.plistBundle display name的值为 $(APP_NAME)

测试是否生效

Scheme页面分别选择debugreleasemamba三中不同的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.xconfigMamba.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会报错,暴露自定义键时的语法:宏名='$(key)',在代码或其他地方使用宏名来引用,'$(key)':通过key来指定每个模式下的对应的自定义键的名字,通常将宏的名字和key的名字保持一致, 注意 等号前后一定不能有空,Common.xconfig中第一个keyGCC_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编译阶段就能运行脚本,我们需要将脚本插入到XcodeBuild Phases中,首先我们先新建一个Build Phases如下所示:

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

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

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


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


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

script的实际运用

利用script来实现每当build的时候改变 Info.plistBundle 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中的CFBundleShortVersionStringCFBundleVersion变量,并根据CONFIGURATION配置的是Release还是Debug来修改对应的BundleVersion,至此每当build时都会改变相应的BuildVersion值。

总结

本文主要介绍了利用xconfig文件如何进行项目的动态配置,并进行了实际的演示,同时介绍了scriptXcode中编译的基本使用,并配合xconfig文件能让Xcode在编译前做更多有意义的事情,更多关于iOS使用xconfig script脚本的资料请关注程序员之家其它相关文章!

相关文章

  • 总结IOS中隐藏软键盘的三种方式

    总结IOS中隐藏软键盘的三种方式

    在IOS开发中,软键盘是开发者们经常需要打交道的地方,下面为大家带来我整理总结的三种隐藏键盘的方法。有需要的可以参考借鉴。
    2016-08-08
  • 针对iOS10新增Api的详细研究

    针对iOS10新增Api的详细研究

    这篇文章主要针对iOS10新增Api进行详细研究,基于Api层面,着重看一些具体用法所做的笔记,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • 谈谈iOS中的多继承与多重代理

    谈谈iOS中的多继承与多重代理

    这篇文章主要给大家介绍了关于iOS中多继承与多重代理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-10-10
  • 详解iOS 轻松获取当前控制器的正确方式

    详解iOS 轻松获取当前控制器的正确方式

    这篇文章主要介绍了详解iOS 轻松获取当前控制器的正确方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • iOS开发中runtime常用的几种方法示例总结

    iOS开发中runtime常用的几种方法示例总结

    Runtime也就是所谓的“运行时”,因为是在运行时实现的。下面这篇文章主要给大家介绍了关于iOS开发中runtime常用的几种方法的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2018-08-08
  • iOS大文件的分片上传和断点上传的实现代码

    iOS大文件的分片上传和断点上传的实现代码

    这篇文章主要介绍了iOS大文件的分片上传和断点上传的实现代码,需要的朋友可以参考下
    2017-12-12
  • 使用UItableview在iOS应用开发中实现好友列表功能

    使用UItableview在iOS应用开发中实现好友列表功能

    这篇文章主要介绍了使用UItableview在iOS应用开发中实现一个好友列表功能的方法,代码基于传统的Objective-C,需要的朋友可以参考下
    2015-12-12
  • iOS App设计模式开发中策略模式的实现示例

    iOS App设计模式开发中策略模式的实现示例

    这篇文章主要介绍了iOS App设计模式开发中策略模式的实现示例,例子采用传统的Objective-C语言编写,需要的朋友可以参考下
    2016-03-03
  • Objective-C实现身份证验证的方法示例

    Objective-C实现身份证验证的方法示例

    这篇文章主要给大家分享了Objective-C实现身份证验证的方法,文中给出了详细的示例代码,对大家具有一定的参考价值,需要的朋友们下面来一起看看吧。
    2017-03-03
  • 超全的iOS各种设备信息获取方法总结(包括iPhone8/iPhone X)

    超全的iOS各种设备信息获取方法总结(包括iPhone8/iPhone X)

    这篇文章主要给大家介绍了关于iOS各种设备信息获取方法,iPhone8/iPhone X的后驱详细信息也已更新,文中给出了详细的示例代码供大家参考学习,需要的朋友们下面随着小编来一起学习学习吧。
    2017-12-12

最新评论

?


http://www.vxiaotou.com