Skip to content

Instantly share code, notes, and snippets.

@niw
Last active March 23, 2020 21:31
Show Gist options
  • Save niw/74cd5d058f9ebb65c2274bd615d0489a to your computer and use it in GitHub Desktop.
Save niw/74cd5d058f9ebb65c2274bd615d0489a to your computer and use it in GitHub Desktop.
How to embed user-specific dynamic variables updated every build in `Info.plist`

How to embed user-specific dynamic variables updated every build in Info.plist

Info.plist can contain many dynamic variables that you can specify in Xcode configuration file by using Build Settings parameters.

However, Xcode configuration is a static environment and you can NOT inject any dynamic variables except Build Settings parameters.

One of solutions is using Pre-actions in Build scheme to update Xcode configuration file dynamically, however, it creates invisible dependency between scheme configuration and each project configuration.

Use Info.plist preprocessor

Actually, Xcode has a feature to preprocess Info.plist using its clang preprocessor with external prefix header file. Thus, using this feature, we can preprocess Info.plist with updated prefix header file.

Add place holders in Info.plist

In your Info.plist, add placeholders as like regular macro placeholder. For example, let’s embed a build timestamp.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    ...
    <key>MYBuildTimestamp</key>
    <string>__BUILD_TIMESTAMP__</string>
</dict>

Note that these values are readable using Bundle from the application.

Add Build Settings

First, you need to add next Build Settings to your Xcode configuration file for your project target.

INFOPLIST_PREPROCESS = YES
INFOPLIST_PREFIX_HEADER = "$(DERIVED_FILE_DIR)/InfoPlist-Prefix.h"

This enables Info.plist preprocessing with InfoPlist-Prefix.h prefix header file.

You MAY also need to add next configuration to make preprocessor works with specific string such as http://url..., which //url... to the end of line will be removed without this option because it is considered as a comment by the clang preprocessor.

INFOPLIST_OTHER_PREPROCESSOR_FLAGS = -traditional

Update Build Phases

Then, add "Run Script" build phase to your target that has this Xcode configuration. In this build phase, you MUST add next files to "Output Files".

$(INFOPLIST_PREFIX_HEADER)
$(INFOPLIST_FILE)

Here, this $(INFOPLIST_FILE) is not actual output file, however this is required to let Xcode runs this Run Script build phase prior to Info.plist preprocessing. Note that $(INFOPLIST_PREFIX_HEADER) is optional, actually Xcode doesn't recognize this file as a dependency.

Also, you SHOULD add next file to "Input Files".

/dev/null

Without this blank input file, Xcode MAY NOT run this Run Script build phrase.

Add script to update prefix header file

Now you can do whatever you want to update prefix header to define macro placeholder.

#!/usr/bin/env bash

readonly BUILD_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)

cat <<-EOF > "${INFOPLIST_PREFIX_HEADER}"
#define __BUILD_TIMESTAMP__ ${BUILD_TIMESTAMP}
EOF

With this setup, every time when Xcode build the target, it will update Info.plist with current time.

Tested environment

This behavior is tested on Xcode 11.4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment