Generating Code from Xcode Configurations

Hamdullah Shah
4 min readJun 28, 2018

--

If you are also using xcconfig files to manage different environment beta , staging and live Then you might have notice that reading the values from xcconfig by plist is not really a clean way and it also doesn’t provide compile time safety. Because sometime you forgot to add the config values to other environments and preprocessor macros doesn’t work anymore to read these values.

So why not convert them into a Swift struct at compile time with a build script ?

Following is a sample of debug.xcconfig which contains some custom config values forAPI_HOST and GMS_API_KEY

exposing config env values in plist

To read the values from the config the old way is to add entries in the info.plist and then read them in the code runtime. If the values wasn’t in the specific config then the app will crash at runtime.

reading config values from plist

All these configuration that we declared end up as environment variables. For example to read the whole build settings run the following command in your terminal. It will print all the configs including the one we added in xcconfig file.

print buildsettings

We can use a prefix e.g buidlConfig_ to the configuration variables so that we can parse them in the output by using grep

Now executing the following command will print the lines which include buildConfig_ prefix only.

xcodebuild -workspace MovieDB.xcworkspace -scheme "MovieDB" -showBuildSettings | grep "buildConfig_"

Now by using sed we will replace buildConfig_ prefix with static let and add "" to the right hand side considering everything on the right is String

sed "s/buildConfig_/static let /"#for double qoutes
sed -E 's/= (.*)/= "\1"/'

This is how the whole command will look like now

xcodebuild -workspace MovieDB.xcworkspace -scheme "MovieDB" -showBuildSettings | grep "buildConfig_" | sed "s/buildConfig_/static let /" | sed -E 's/= (.*)/= "\1"/'
output after executing the whole commmand

Next let’s put this command in a script file and generate the Swift struct from it. Create a new file in your project and select the Shell Script from the template name itBuildConfig After creating the shell script we have to make it executable by running the following command

chmod +x BuildConfig.sh

Copy paste the following command which include some echo and printf to create some Swift code syntax.

During the build phase build settings output can be accessed simply by running env replacing the xcodebuild with that will look like the following.

echo "//Generated file please do not edit"printf "//\n//\n//generated by BuildConfig.sh\n"printf "import Foundation\n\n"echo "struct BuildConfig { "echo "  fileprivate init() {}"env |\grep "buildConfig_" | \sed "s/buildConfig_/    static let /" |\sed -E 's/=(.*)/ = "\1"/'echo "}"

Create a new run script in Build Phases and place it before the Compile Sources and copy paste the following command in it. It will write the output to the file at Project_Root/MovieDB/BuildConfig.generated.swift

"$PROJECT_DIR/BuildConfigs/BuildConfig.sh" > "${PROJECT_DIR}/MovieDB/BuildConfig.generated.swift"

Drag and drop the the generated file from finder in the Xcode project so that it can be referenced.

add the the file BuildConfig.generated.swiftto gitignore so that it can be generated on each build time. Otherwise you will see this file changes all the time as you will be frequently switching between debug and release configurations.

🍒 on the top ?

Let’s convert the urls to it’s type

another sed ;)

sed -E 's/"http.*/URL(string:&)!/'

adding this to the same file will become

echo "//Generated file please do not edit"printf "//\n//\n//generated by BuildConfig.sh\n"printf "import Foundation\n\n"echo "struct BuildConfig { "echo "  fileprivate init() {}"env |\grep "buildConfig_" | \sed "s/buildConfig_/    static let /" | \sed -E 's/=(.*)/ = "\1"/' | \sed -E 's/"http.*/URL(string:&)!/'echo "}"

This is how the final the finalBuildConfig.generated.swift will look like

Finally if there is something missing in any of the environment configuration then we will get a compile time error instead of run time and no more polluting of info.plist

To see all together in action here is the link to Github repo.

--

--