Preprocessor Macros in Pods

| /

As mentioned earlier, we can inject macros into Pods from the outside by modifying the Podfile. You can refer to this article Inject TEST macros into Pods.

Sometimes there are some macros that are expected to be used inside the Pods library we wrote ourselves.

A common practice, just like ordinary business code, define a macro definition header file, and then import.

But sometimes there are such scenarios, such as logging or providing some externally exposed APIs, you need to provide the version of the current library.

At first, it may be considered to manually modify the version information for each release.

But in many large companies, the version upgrade of the component library is done automatically by build tools, build platforms, etc.

These common CI/CD tools and platforms will intelligently update the version number in the podspec file. But it can only do so much.

It is not a good practice to predict the version number that may be upgraded in advance and encode it into the code. In particular, when the build fails, the version number jumps, or some unexpected version number changes.

In fact, what we expect is to be able to generate precompiled macros directly from the information in the podspec, as well as define macros directly in the podspec.

We can learn about reasonable and correct practices by consulting the official documentation of CocoaPods and learning from many excellent Pods libraries.

What we have to do is inject macros through xcconfig and configure our xcconfig through podspec.

It should be pointed out that both Podfile files and podspec files are essentially ruby scripts. Knowing this will also be of great help for us to understand the working mode of CocoaPods.

Standard practice is described directly below.

We can customize xcconfig through the xcconfig configuration in podspec.

When configuring xcconfig, we can use various configurations in podspec, such as s.version.

The sample code is as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Pod::Spec.new do |s|
s.name = 'CKQTestKit'
s.version = '1.1.7'
s.summary = 'CKQTestKit.'

s.description = <<-DESC
CKQTestKit。
DESC

s.homepage = 'http://'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Wang Qing' => 'wq.cyan@gmail.com' }
s.source = { :git => 'ssh://', :tag => s.version.to_s }

s.ios.deployment_target = '9.0'

s.source_files = 'CKQTestKit/Classes/**/*'
s.xcconfig = { "GCC_PREPROCESSOR_DEFINITIONS" => "CKQ_TESTKIT_VERSION=\\@\\\"#{s.version.to_s}\\\"" }

s.resource_bundles = {
'CKQTestKit' => ['CKQTestKit/Assets/js/*.js']
}

s.dependency 'Mantle', '>= 1.5'
end

After pod install, the corresponding content in the actually generated xcconfig is as follows.

1
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 CKQ_TESTKIT_VERSION=\@\"1.1.7\"

The actual generated xcconfig file can be found in the Support Files of the subproject corresponding to the final generated Pods.

What we need to pay attention to is the backslash \ used for escaping inside.

When we inject string macros through xcconfig, both @ and " need to be escaped.

And when this code is put into the podspec and wrapped in double quotes, \ and " need to be escaped.

1
@"x" -> \@\"x\" -> \\@\\\"x\\\"
flowchart LR
  A["@#quot;x#quot;"]--> B["\@\#quot;x\#quot;"]
  B--> C["\\@\\\#quot;x\\\#quot;"]

And, in the podspec configuration, we can use #{s.version.to_s} to construct the corresponding configuration using the s.version of the current configuration.

In this way, we can directly use the CKQ_TESTKIT_VERSION macro to get the current library version in our code, and save the extra cost of manual maintenance.