CICD P2. Multiple Firebase Environments in Flutter

Aurimas Deimantas
6 min readMar 2, 2021

This tutorial will cover the use-case of having two or more Firebase environments and possibility of having each environment app installed to your phone. It is extremely useful when one wants to keep their production app in phone and simultaneously continue to develop and build their build under development app.

In the scenario of having multiple Firebase environments and each holding same bundle id/package name — after building your development app, you’ll be forced to uninstall your production app.

If you are not interested in keeping standalone apps with their environment installed in your phone but still would like to manage multiple Firebase environments easier — give it a try with my previous article.

At very first, let’s create our Firebase projects and add Android and iOS apps inside. Make sure you have different bundle id/package name for development and production environments.

While working with Joe Colon and NativoPlus, we always try to push boundaries of the app development thus we are very widely utilising this structure.

Generating environment files

If you have read my previous article — this script won’t be something new. It’s more advance version of previous one.

How to set it up:

  1. Create a new folder rootApp/tools/environment-generator
  2. Clone repo contents to this folder. Main ts file should appear in rootApp/tools/environment-generator/environment-generator.ts
  3. Add your plist and/or json in the same path with correct suffix ( dev or prod ). Production files will have a suffix -prod , Development files will have a suffix -dev .
    You can get json file from Firebase while setting up an Android app.
    You can get plist file from Firebase while setting up an iOS app.

End result file structure should look like this:

This script does two things:

  1. Setting up correct Firebase environment with generating correct files;
  2. Generates specific variables for the environment. For now we want our appName and packageName / bundleId be different per each environment.

Firebase environment setup

What script does, is simply detects, which environment you want to build you app in and copies correct file into correct folder within android or ios folders.

For example, you run node environment-generator dev .

  1. It locates google-services-dev.json and places it to ../android/app path naming google-services.json .
  2. It locates GoogleService-Info-dev.plist and places it to ../ios/Runner path naming GoogleService-Info.plist .

That’s all it is.

Generating specific variables for environment

Android

Go to your AndroidManifest.xml and update android:label to android:label="@string/app_name” .

What it will do, it will take app_name from the build_gradle and put it as an App name.

Go to rootApp/android/app/build.gradle and find android{} section. Within this tag we need to include

def envProperties = new Properties()
def envPropertiesFile = rootProject.file('env.properties')
if (envPropertiesFile.exists()) {
envProperties.load(new FileInputStream(envPropertiesFile))
}

It will look something like this

env.properties file will be automatically generated once we run our script. It will act as environment variables holder. And values from this file will get extracted in build.grade when app will be building.What it does, it will locate our env.properties file end now we will be able to extract values from it.

Now within same build.gradle find defaultConfig{} section. Here we will update with our new packageId and appName .

applicationIdSuffix envProperties["appSuffix"]
resValue "string", "app_name", envProperties["appName"]

It will looks something like this

Since we already added app_name reference in AndroidManifest.xml , here how it wires our appName to there.
As for appSuffix , our original packageId will be amended with it. In our case for development build we will have surge.gentlebirth.app.dev .

That’s it for Android.

iOS

Go to rootApp/ios/Flutter and create env-defaults.xcconfig.

It will look something like this

env-defaults.xcconfig is the default environment variables holder . Inside let’s put

appName=DEV
appSuffix=.dev

Now locate Debug.xcconfig and Release.xcconfig or any others, depending on your xCode Schemas and include

#include "env-defaults.xcconfig"
#include "env.xcconfig"

To each of them.

We didn’t do anything with env.xcconfig yet — no worries. This file will get automatically generated when our script will be ran. It is an analog file to env.properties on Android side.

Now go to rootApp/ios/Runner.xcodeproj/xcshareddata/project.pbxproj .

Find the line containing PRODUCT_BUNDLE_IDENTIFIER (it should be in more than one place) and update it adding $(appSuffix) .
Since we are using dynamic value, don’t forget to put whole bundle within double quotes (").

It will take that appSuffix value from env.xcconfig file while generating the build.

For the app name, lets head to rootApp/ios/Runner/info.plist and find CFBundleName . Add $(appName) as a value.

Wiring everything together

Go to rootApp/tools/environment-generator and run node environment-generator dev .

If all good, you will see following output in the console:

*********************************************
Generating environment files and configs. Environment: dev
Generating OS env files. Content:
appName=DEV
appSuffix=.dev
*********************************************
google-services-dev.json file copied successfully.
GoogleService-Info-dev.plist file copied successfully.
env.properties file generated for dev environment.
env.xcconfig file generated for dev environment.

And after building the app, you can clearly see, which is environment was used based of the app name. Now you can enjoy having both apps on the device.

Script explanation part two

First part of the script was explained previously.
Second part is slightly more complex, but still fairly easy to understand and maintain, if needed.

This function takes care of generating correct Android or iOS file and placing into correct path.

We can manually change values upon our needs here.

These values will get generated into both — env.properties and env.xcconfig — as a text.

BONUS — Generating environment upon running the app

Check BONUS section (end of article) how to do it.

That’s it. Hopefully this guide can save you tons of hours in finding the right solution for mentioned use-case.

Up next — starting CICD real journey with multiple environments.

--

--

Aurimas Deimantas

I build kick-ass mobile apps @ https://deimantas.dev || Product Virtuoso and Startup Freak