Created By Mitchell Tilbrook,

Senior Mobile Developer @ Zip

April 7, 2021

Firebase

Firebase is a suite of mobile cloud tools for iOS, Android and Web from Google. Amount these tools is crashlytics, a simple crash reporting tool. To start recording errors with Firebase crashlytics we can create a firebase project per deployment environment. Let's assume we have three app environments, develop, staging, and production. Each app environment will have a unique app/bundle id to install all variances on a single test device.

The Multiple Firebase Environments Problem

Because each Firebase project has unique keys for each environment, we have multiple configurations that we need to ship with each app variants. On Android, this is easy as the Firebase google-services.json will contain all variance, and Gradle will pick out the one specific to the App ID we are compiling.

On iOS, on the other hand, we have a single GoogleService-Info.plist per variant. So, we need a way to pick the right plist per variant.

Solutions

Multiple Targets

A common method I've seen to address the variance problem is to create an Xcode Target per environment. We then copy the variant plist into each targets source folder and add the Build Phase task to each Target with the input plsit file. While the multi-target method works, it does with the cost of adding a Target purply for a config file, we then need to duplicate the Build Phase task for each Target. The one upside I've seen is that it's quick swapping Targets in Xcode to select a variant; I find the duplications costs more than it should

Parametrized Builds

We parametrize the build pipeline using environment variables. The best way I've found so far to inject the environment variables is to either pass them in a compile-time or defined them in the bash profile, whichever works best for your setup.

To make the build parametrize manageably, we need to set up a script that will export environment variables for the build to use.

So, we start by creating a shell file at the root of your project Configs/env-import.sh. The shell file is not strictly required as you could define the variables to your shell config if you only have a single project. When working in a team or on multiple projects having a shell file works well as a central documentation/edit point

#!/usr/bin/env sh
# $SRCROOT/Configs/env-import.sh
 
DEV_BUILD_VARIANCE="dev"
STAG_BUILD_VARIANCE="stag"
PROD_BUILD_VARIANCE="prod"
# MY_PROJECT_BUILD_VARIANCE=

export FIREBASE_BUILD_VARIANCE=${MY_PROJECT_BUILD_VARIANCE:-DEV_BUILD_VARIANCE}

Next we want to add a Pre-Actions to our build to invoke our Pre-Actions/firebase-config.sh that we will define after this. I recommend creating a shell file here over just editing the build schema as it makes Git diffing and shell linting (Shellcheck) easier.

"${SRCROOT}/Pre-Actions/firebase-config.sh"

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/253d3557-8508-488c-9d73-b7e444055c15/pre-action.png

Now we can define the Pre-Actions/firebase-config.sh we could copy the plist into the project. Here I've opted to use PlistBuddy to update a git ignored GoogleService-Info.plist in my source root; you can also copy the plist file if you prefer;

firebase-config.sh will copy the keys that differ between the info files using PlistBudy