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.
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.
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.
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
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.
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.
This behavior is tested on Xcode 11.4.