When developing apps that connect to a server, mostly we have a staging server and a production server. As such we need two app versions, one the dev version that connects to the staging server and the other that connects to the prod. These apps could have different configs to load, especially if it's based on Firebase. Firebase provides a GoogleService-Info.plist
file for each project. Below demonstrates how we will use this as a use-case to further demonstrate working with multiple app configurations, schemas and Xcode config file, but with a single target.
1. First, download the Firebase config for both dev and prod to
Config
folder under the project root and name them as
GoogleService-Dev-Info.plist
and
GoogleService-Prod-Info.plist
respectively.
2. Under the Xcode configuration for the project, click on the
Project > Info
and under
Configurations
, we need to add one each for
Dev Debug
and
Prod Release
. For that, from
Editor
choose
Add Configuration > Duplicate "Debug" Configuration
and name it
Dev Debug
and follow the same for prod, but use
Duplicate "Release" Configuration
option.
3. Now under the
Build Settings
tab, we will add an
ENV
flag. For that click the plus icon next to
Levels
in the header menu and choose
Add User-Defined Settings
with the following values.
Dev Debug DEV_DB
Dev Release DEV_RELEASE
Prod Debug PROD_DBG
Prod Release PROD_RELEASE
4. Next click on the app name under the Target
section and choose Build Phases
and add a new Run Script Phase
with the following content.
if [ "$ENV" == "DEV_DBG" ]; then
echo "Dev debug config found"
cp -r "${PROJECT_DIR}/Config/GoogleService-Dev-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
echo "Firebase staging plist copied."
elif [ "$ENV" == "PROD_RELEASE" ]; then
echo "Prod release config found."
cp -r "${PROJECT_DIR}/Config/GoogleService-Prod-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
echo "Firebase production plist copied."
fi

This will copy the right plist and place it under the .app
folder with plist renamed to GoogleService-Info.plist
. This makes the call to [FIRApp configure];
to work without having to manually load the plist and specify the credential info.
5. Next, we will create multiple schemes, one for building the staging version and the other for prod. For that, click on the target next to the run-stop, button and choose New Scheme
. For the target, choose the target app and let's name the schemes as Dev Debug FBApp
and Prod Release FBApp
.
6. We need to set the right configuration for each scheme. For that click
Edit scheme
, and for the debug scheme, under the
Run
section, for
Build Configuration
, choose
Dev Debug
and for the production scheme, choose,
Prod Release
. Under
Manage schemes
, make sure these schemes have
Shared
enabled.
With this, we have Firebase setup working with multiple environments. However, if we use other Firebase options like
FirebaseAuth
, we need to add additional settings like
URL Type
so that after the user sign-in, the invoking the app works. For this, we need to create a new
xcconfig
as described next.
From
File > New File > File
, choose
Configuration Settings File
and name it as
DevConfig.xcconfig
and another with
ProdConfig.xcconfig
. Do not add the
xcconfig
file to any target as it's a build config file and we don't want it to be shipped with the app bundle. The content of the dev config follows.
APP_NAME = FBAppDev
APP_BUNDLE_ID = org.example.app.FBAppDev
APP_FIREBASE_URL_SCHEME = com.googleusercontent.apps.25135051042-2utrt0j007lp7vf23p73fd306trszqn2
#include "Pods/Target Support Files/Pods-FBApp/Pods-FBApp.dev debug.xcconfig"
Note that in case of URLs in the
xcconfig
, we need to escape it like
APP_SERVER_URL_LOCAL = http:/$()/localhost:4000/
.
Here we define a variable
APP_FIREBASE_URL_SCHEME
with value taken from the reverse client ID of the dev plist. We will give a different bundle ID so that we can have both staging and prod versions of the app installed, without one overwriting the other.
7. We need to specify the
xcconfig
file for each project configuration we created before (in step 2). Under the
Project > Configurations
, for
Dev Debug
, expand and choose the
DevConfig
for the
Based on Configuration File
value. Same for the prod configuration name.
8. If the project uses Cocoapods, first close the workspace, delete the
xcworkspace
file, the
Pods
directory and the
Podfile.lock
and run
pod install
which will install the dependencies as usual and generate the Pod xcconfig files for each configuration we have, but does not set it as we have already set a config. So we include the corresponding Pod config in the xcconfig using the
#include
.
9. For the final step, we need to parameterise the variables in the
Info.plist
file as follows.
Bundle identifier $(APP_BUNDLE_ID)
Bundle name $(APP_NAME)
URL Types > URL Schemes $(APP_FIREBASE_URL_SCHEME)
Voila, we now have a project setup with different configurations with a single app target.