When developing a project, we need to build multiple versions, and each version will have individual environment settings. For example, the development version connects to the development server, and the QA version connects to the QA testing server. This article will explain how to use Xcode’s Configuration and Scheme to build multiple versions.
The complete code for this chapter can be found in .
Table of Contents
- Overview
- Create Configuration Files (.xcconfig)
- Creating Builds
- Creating Schemes
- Adding Settings in the Configuration file
- Using a Different App Name for Each Version
- Using a Different Bundle Identifier for Each Version
- Programmatically Accessing the Settings in the Configuration Files
- Using Different Resource Files for Each Version
- Conclusion
- refer to
Overview
In this article, we will create two versions, Develop and Production. The Develop version is for development, and the Production version is for the Apple Store. Each version will have Debug build and Release build.
Create Configuration Files (.xcconfig)
First, we need to create individual configuration files for the Develop and Production versions. Add a Configuration Settings File to the project.
Enter Develop.xcconfig as the file name. In the red box, do not select any targets, because we do not want to package the Develop.xcconfig into the targets. After creation, repeat the same steps to create a Configuration Settings File called Production.xcconfig.
The Develop version will use Develop.xcconfig, and the Production version will use Production.xcconfig.
Creating Builds
Next, we need to assign configuration files to each build.
Xcode creates Debug and Release builds by default. We renamed Debug and Release to DevDebug and DevRelease respectively.
Click the +
button, select Duplicate DevDebug Configuration
, and name it ProDebug. Click again, select Duplicate DevRelease Configuration
, and name it ProRelease.
So, we have four builds in total.
Assign Develop.xcconfig to DevDebug and DevRelease, and Production.xcconfig to ProDebug and ProRelease.
Creating Schemes
Finally, we assign each scheme its Debug and Release builds.
Click Manage Scheme
.
Rename the existing “ConfigurationExample” scheme to “Develop ConfigurationExample”. As the name suggests, it is for the Develop version.
Confirm that the “Develop ConfigurationExample” scheme uses DevDebug and DevRelease builds.
Next, we need to create a scheme for the Production version. Click New Scheme
.
Enter “Production ConfigurationExample”.
After creation, assign all the builds of the “Production ConfigurationExample” scheme as ProDebug and ProRelease builds.
Adding Settings in the Configuration file
Now let’s adding a new value to a setting in the configuration file.
Configuration files use the following format:
<SettingName> = <SettingValue>
If you set a value to a setting that does not exist, it declares a new setting.
NEW_SETTING = Hello Wayne's Talk
If you set a value to an existing setting, it will overwrite the original value of the setting. We can also use $(inherited)
to get the original value of the setting. You can find more pre-defined settings in the Build settings reference.
OTHER_SWIFT_FLAGS = $(inherited) -v
We declare two new settings in Develop.xcconfig and Production.xcconfig: APP_NAME
and APP_ENV
. The next part of this article will explain how to use these two settings.
// Develop.xcconfig APP_NAME = Develop APP_ENV = DEV
// Production.xcconfig APP_NAME = Production APP_ENV = PRO
Using a Different App Name for Each Version
Now we have two versions. We want different versions to display different App names.
In TARGETS -> ConfigurationExample -> Build Settings
, search for PRODUCT_NAME
.
Change the original value $(TARGET_NAME)
to $(APP_NAME)
. APP_NAME
is the setting we just added in the configuration file. You can try to run “Develop ConfigurationExample” and “Production ConfigurationExample” to see if their App names have changed.
Using a Different Bundle Identifier for Each Version
We may want to install all versions to a device at the same time. In order to do that, we must set a different Bundle Identifier for each version.
In TARGETs -> ConfigurationExample -> Signing & Capabilities
, we found that each build has an individual page. In the DevDebug and DevRelease pages, set the Bundle Identifier to com.waynestalk.ConfigurationExample.dev
.
In the ProDebug and ProRelease pages, set the Bundle Identifier to com.waynestalk.ConfigurationExample.pro
.
Switch to the All
page, you can see all Bundle Identifiers.
Programmatically Accessing the Settings in the Configuration Files
It is not possible to directly access the settings in the configuration file programmatically. However, we can do this by adding a setting to Info
. In this way, we can access it programmatically.
In TARGETS -> ConfigurationExample -> Info
, add a new key named AppEnv
and set its value as $(APP_ENV)
. APP_ENV
is the setting we just added in the configuration file.
Then, we can access AppEnv
programmatically, as the following code.
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let env = (Bundle.main.infoDictionary!["AppEnv"] as! String) print(env) } }
Using Different Resource Files for Each Version
Sometimes we need to use different resource files in each version, such as Firebase’s GoogleService-Info.plist.
As we know, a configuration file is a text file. So the easiest way is to add resource files of all versions to the project and build them into the app. Then, set the path of the corresponding resource file for each version in the configuration file. In this way, at runtime, the app reads the path of the resource file, and then loads the resource file.
However, the above method has a disadvantage that we must build the resource files of all versions into the app.
We will introduce another method. This method is to add resource files of all versions to the project, but do not build them into the app. When building, copy the corresponding resource files to the build folder.
Below we will take Firebase’s GoogleService-Info.plist as an example.
First, add GoogleService-Info.plist of the Develop version to the project, and check Add to targets
.
Under the project directory, add the Develop/
and Production/
folders. Add the Develop version of GoogleService-Info.plist to the Develop/
folder, and uncheck Add to targets
. Then add the Production version of GoogleService-Info.plist to the Production/
folder, and uncheck Add to targets
.
After the above is completed, we can see that there are three GoogleService-Info.plist in the project. But there is only one GoogleService-Info.plist in the Copy Bundle Resources
list.
Under TARGETS -> ConfigurationExample -> Build Phrases
, click the +
button in the upper left corner, then click New Run Script Phrase
.
Paste the following code into the Run Script Phrase
. In the code, we copy the corresponding GoogleService-Info.plist to the build folder according to ${CONFIGURATION}
.
echo "Copy GoogleService-info.plist" echo "Configuration is ${CONFIGURATION}" set -x case "${CONFIGURATION}" in "DevDebug" | "DevRelease" ) cp "${PROJECT_DIR}/ConfigurationExample/Develop/GoogleService-info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-info.plist" ;; "ProDebug" | "ProRelease" ) cp "${PROJECT_DIR}/ConfigurationExample/Production/GoogleService-info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-info.plist" ;; *) ;; esac
Finally let’s try to build the project. We can see that when building, the corresponding GoogleService-Info.plist is copied to the build folder.
Conclusion
When developing projects, we often need to have multiple versions of environment settings. Xcode’s configuration and scheme let us do it easily. In addition, it is easier to manage each environment setting in the configuration file.
refer to
- Configuring the build settings of a target , Apple Developer.
- Adding a build configuration file to your project , Apple Developer.
- Build settings reference , Apple Developer.
- StackOverflow: https://stackoverflow.com/a/48789232/841963