Generating Code from Xcode Configurations
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
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.
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.
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"/'
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.swift
to 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.