For two days, I see captures and memes about the error that occurred in the Airbnb mobile application. A notification with the message "Test dev" was sent to several users.
I don't know how it happened, but I'm sure this time it's not an intern 😅 anyway ! In this article I'm going to show you how at BeServed, we avoid these kinds of errors by creating two Firebase instances for a single Android project, one of which is for testing and one for production.
By default, when we want to add a Firebase project to our Android application, the Firebase guide asks us to add the plugin com.google.gms.google-services
to our app build.gradle
file, to download and put the google-services.json
file in the app folder and thanks to that your application will be linked to the Firebase project.
Now the question is 👇🏾
How to link one android app to two Firebase projects
There are two possibilities,
1st option : using multiple google-services.json
The first one is to use a google-services.json
file for each build variant.
Indeed, you can place several google-services.json
files each in a folder named according to the build variant it belongs to.
For example, if you have product flavors "dev" and "release", you can organize them as follows :
app/
google-services.json
src/dev/google-services.json
src/release/google-services.json
...
The google-service.json
file is processed into Android string resources by the Google Services gradle plugin. You can see which resources are created in the Google Services Plugin documentation on Processing the JSON file.
To learn more, see the Google Services Plugin documentation on Adding the JSON file.
These resources are then loaded by the FirebaseInitProvider
which runs before your application code and initializes Firebase APIs using those values.
2nd option : checking at runtime with Firebase options object
As you my already know, the JSON file is processed at build time, and it's read into an options object (FirebaseOptions
) that is referenced by the Firebase application object (FirebaseApp
).
This option consists in adding the string resources directly to your app instead of using the Google Services Gradle plugin. And this is how we do it at BeServed.
First you should
- Remove the
google-services
plugin from your rootbuild.gradle
- Delete the
google-services.json
from your project - Delete
apply plugin: com.google.gms.google-services
from your appbuild.gradle
After that, you need to create a Firebase options object to hold the configuration data for the Firebase application. It uses the builder pattern, and you can specify a couple of information, as in the example below.
val options = FirebaseOptions.Builder()
.setProjectId("my-firebase-project")
.setApplicationId("1:27992087142:android:ce3b6448250083d1")
.setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
.build()
Project ID, Application ID and API key are required fields, but there’s a lot of other that can be found in the API reference documentation
After creating an option variable for each instance, this is how our Application class should look like :
class App : Application() {
override fun onCreate() {
super.onCreate()
val devOptions = FirebaseOptions.Builder()
.setProjectId("beserved-development-project")
.setApplicationId("1:212877806437:android:fee5f52927e2f7a907ed8c")
.setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
.build()
val prodOptions = FirebaseOptions.Builder()
.setProjectId("beserved-production-project")
.setApplicationId("1:27992087142:android:ce3b6448250083d1")
.setApiKey("BIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
.build()
// here we check wich instance to use depending on the build type
if(BuildConfig.DEBUG) {
FirebaseApp.initializeApp(this, devOptions, "beserved-development")
} else {
FirebaseApp.initializeApp(this, prodOptions, "beserved-production")
}
}
}
Now our app will dynamically switch between Firebase projects and the third parameter of the initializeApp()
is the instance name, so if for example we want to use the Firestore we should provide the name of the corresponding instance, and we can also change this name dynamically by checking if the BuildConfig
is DEBUG or not.
val instanceName = if (BuildConfig.DEBUG) "beserved-development" else "beserved-production"
FirebaseApp.getInstance(instanceName)
The next challenge is to keep settings synchronized on both test and development instances in order to avoid unexpected behavior when you switch between build variants. But this will prevent you to test in production, event if you're an intern !!
I hope this article was helpful !