Closed approached closed 4 years ago
I think there is some commentary about this in older issues if you search, we should probably put a FAQ or guide up
There are a couple ways to do it but they all start with the same idea: you have multiple firebase projects in the cloud, one for each environment you want to support.
Then for Android I think the best thing is to use build flavors, and maintain the google json file in for example android/app/src/environment name
/google-services.json - one for each environment. So I have:
mike@isabela:~/work/Kullki/ksocialscore/packages/public-app (rebranding *) % find . | grep google-services.json
./android/app/src/qa/google-services.json
./android/app/src/staging/google-services.json
./android/app/src/prod/google-services.json
./android/app/src/dev/google-services.json
I use product flavors (google up the Android docs for them) - which are a separate axis from "release" vs "debug" so that each environment get's a unique application Id and I can have all of the environments installed on the same device at the same time.
So my android/app/build.gradle has this chunk in it:
flavorDimensions "release_streams"
productFlavors {
dev {
applicationIdSuffix ".dev"
versionNameSuffix applicationIdSuffix
}
qa {
applicationIdSuffix ".test"
versionNameSuffix applicationIdSuffix
}
staging {
applicationIdSuffix ".staging"
versionNameSuffix applicationIdSuffix
}
prod {
}
}
For iOS I use Targets in Xcode as the build separation (they work like flavors in Android's gradle build system - make one per backend Firebase project/environment), and I made a sub-directory for each GoogleService-Info.plist. Then for each environment Target in Xcode I select the correct plist file and select that it should be a member of the target
So I have
mike@isabela:~/work/Kullki/ksocialscore/packages/public-app (rebranding *) % find . |grep GoogleService-Info.plist
./ios/KScoreApp/Prod/GoogleService-Info.plist
./ios/KScoreApp/Dev/GoogleService-Info.plist
./ios/KScoreApp/Staging/GoogleService-Info.plist
./ios/KScoreApp/Test/GoogleService-Info.plist
And each of those targets gets a separate bundleId (prod is 'com.kompai.kscore', dev is 'com.kompai.kscore.dev' etc for me) for the same reason as the android flavors above - I can have each environment installed on the same device at the same time. I do have to manage each as a separate product in Apple Developer Connection which is a pain in the butt, but that is mostly one time setup, then they all TestFlight happily.
To make them easy to tell apart I have different icons for the non-prod environments, and I do a bit of work in the app to tell (based on bundleId or applicationId) what flavor is executing so I can enable or disable different features but nothing too fancy.
That's it. All the files are in source control, the firebase descriptor files don't get copied around (that's another way to do it but I don't like "clobber this file with this other file..." as a build step) and I have 4 fully separate apps so they aren't fighting each other for your devices and emulators.
A fair bit of initial infrastructure to build but it's worked great for almost 2 years after getting it going. Hope this helps.
This sounds like solid advice, this should definitely be added to or linked to in the docs
I unfortunately no longer really have perspective on it nor remember all the steps :rofl: - but if you try it and keep track of the steps then propose a docs PR about one person every couple of days (my guess based on support traffic) would probably read it and praise your name...
I think I'll do just that. If I run into problems I'll dm you on discord.
EDIT: There already seems to be a pretty solid guide right here actually.
@mikehardy thank you so much. Your post help me a lot. Your solution works great but i'am a single developer so i have other easy solution.
Android: I use just debug (dev) and release (prod). This blog post help me: https://firebase.googleblog.com/2016/08/organizing-your-firebase-enabled-android-app-builds.html
File structure:
./android/app/src/debug/google-services.json
./android/app/src/release/google-services.json
./android/app/build.gradle
buildTypes {
debug {
signingConfig signingConfigs.debug
applicationIdSuffix ".debug" // <- added
}
For IOS i will try tomorrow
As @approached suggested, on Android the Google Services plugin does support reading from specific build 'type' directory by default, e.g. you can place the json file in a android/app/src/{build_type}
folder if you are using multiple build types.
Source: https://developers.google.com/android/guides/google-services-plugin#troubleshooting
@approached that does seem like it would work, but realize that you won't be properly testing prior to prod, in that release builds typically go through minimizing and obfuscation, which can lead to a whole class of bugs on both platforms. If you don't have a test environment where you test release type builds you may endure some surprises along the way. I think that's a low risk - possible worth it in your situation - but something to keep in mind.
Hello 👋, to help manage issues we automatically close stale issues. This issue has been automatically marked as stale because it has not had activity for quite some time. Has this issue been fixed, or does it still require the community's attention?
This issue will be closed in 15 days if no further activity occurs. Thank you for your contributions.
@mikehardy thank you so much. Your post help me a lot. Your solution works great but i'am a single developer so i have other easy solution.
Android: I use just debug (dev) and release (prod). This blog post help me: https://firebase.googleblog.com/2016/08/organizing-your-firebase-enabled-android-app-builds.html
File structure:
./android/app/src/debug/google-services.json ./android/app/src/release/google-services.json
./android/app/build.gradle
buildTypes { debug { signingConfig signingConfigs.debug applicationIdSuffix ".debug" // <- added }
For IOS i will try tomorrow
Did you find out how to setup it for iOS?
@FRizzonelli I use schemes for iOS. I have one scheme per back-end firebase project. I have one google plist file for each of those firebase projects (naturally). In my Xcode project definition I have associated each particular firebase backend google plist file with the correct scheme via the Xcode "file ownership" feature that lets you associate a specific file with specific scheme(s).
Then when I build for iOS I just choose the correct scheme and I get connected to the correct project.
@mikehardy Do you use the same name for GoogleService-Info.plist
for each variant? (scheme?)
Not sure what "file ownership" is and how it works, I searched in Google and I could not fine something related for XCode.
Do you have any resource where we can read about this?
Thanks!
@afilp no, the variants get suffixes tacked on them for android, so they have different package names. This is critical as it is what allows you to have each variant installed at the same time on the same simulator / real device. Would not be that useful otherwise?
Do you have any resource where we can read about this?
No, sorry. But a simple search turns things up for me? https://stackoverflow.com/questions/14147640/what-is-my-target-and-how-do-i-add-a-file-to-it-for-unit-testing
@mikehardy I know this is a bit old, but I am trying to follow your recommendation for Android.
For me, adding the suffix to the package name, produces the following crash on app start:
java.lang.RuntimeException: Unable to instantiate application com.my.app.adhoc.MainApplication package com.my.app.adhoc: java.lang.ClassNotFoundException: Didn't find class "com.my.app.adhoc.MainApplication" on path: Dex
PathList[[zip file "/data/app/~~cZDyy8K6SOKCxrS3z8Tp4w==/com.my.app.adhoc-wJ2yIcTHXrxufN7F3hmqng==/base.apk"],nativeLibraryDirectories=[/data/app/~~cZDyy8K6SOKCxrS3z8Tp4w==/com.my.app.adhoc-wJ2yIcTHXrxufN7F3hmqng==/lib/arm64, /data/app/~~cZDyy8K6SOKCxrS3z8Tp4w==/com.my.app.adhoc-wJ2yIcTHXrxufN7F3hmqng==/base.apk!/lib/arm64-v8a, /system/lib64, /system_ext/lib64]]
Does this mean I need to change the java source path from android/app/src/main/java/com/my/app/
to android/app/src/main/java/com/my/app/adhoc/
just to support this?
Thanks in advanced!
Hey - I think this guide has all the stuff I had to do for Android, it was just the build flavors that changed, and then running the build flavor ? https://medium.com/@ywongcode/building-multiple-versions-of-a-react-native-app-4361252ddde5
@mikehardy thank you for sharing! That did the trick :D
This was a very helpful guide, thanks @mikehardy. I now have a separate iOS target/bundleId for each environment. As such, I had to adjust my Podfile to handle these new targets, and found this example helpful for that.
Edit: Actually, I think I'm going to use the abstract_target
approach since I don't need to specify many actual pods for my project.
I'll also mention that when you are adding the GoogleService-Info.plist files to the Xcode project, make sure you select "Create groups" and not "Create folder references". If you use folder references then the folder reference will manage the target membership, which resulted in Firebase not finding the plist, causing a crash for me on launch. You want to have /dev, /staging, /prod
as groups so that you can select the target membership on the files directly.
Also note that you'll need to include the appIdSuffix
argument when running the Android variants. So my scripts now look like this (using react-native-config
for the .env files):
"android": "ENVFILE=.env.development react-native run-android --mode=devDebug --appIdSuffix dev",
"android:staging": "ENVFILE=.env.staging react-native run-android --mode=stagingDebug --appIdSuffix staging",
"android:prod": "ENVFILE=.env.production react-native run-android --mode=prodDebug",
Is it still the right way for handling different envs? I read somewhere that now, you should have only one Firebase project and create an application for each env so in the end you have only one google-services.json that handles dev / staging / prod for example.
@Simon-44 Did you find a new way to solve this problem? I've been trying to update my app from RN 68.5 to RN 0.73, but with the new gradle version i need to set a namespace for my app (Removing from manifest). The namespace is different from my aplicationId and i cant resolve this so far keeping the same firebase project. Do you have some tips?
Hi @ all,
How i can use a test and production environment?
I have one codebase which is managed with git. I use follow modules: firestore, authentication, analytics, crashlytics and storage. Firestore and storage is most important.
Has anybody a idea?