CICD P2. Multiple Firebase Environments in Flutter
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:
- Create a new folder
rootApp/tools/environment-generator
- Clone repo contents to this folder. Main
ts
file should appear inrootApp/tools/environment-generator/environment-generator.ts
- Add your
plist
and/orjson
in the same path with correct suffix (dev
orprod
). Production files will have a suffix-prod
, Development files will have a suffix-dev
.
You can getjson
file from Firebase while setting up an Android app.
You can getplist
file from Firebase while setting up an iOS app.
End result file structure should look like this:
This script does two things:
- Setting up correct Firebase environment with generating correct files;
- Generates specific variables for the environment. For now we want our
appName
andpackageName
/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
.
- It locates
google-services-dev.json
and places it to../android/app
path naminggoogle-services.json
. - It locates
GoogleService-Info-dev.plist
and places it to../ios/Runner
path namingGoogleService-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
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.