invertase / flutterfire_cli

A CLI to help with using FlutterFire in your Flutter applications.
Apache License 2.0
164 stars 47 forks source link

Support for multiple scheme dev, staging, prod #14

Closed ghensto closed 1 month ago

Ehesp commented 2 years ago

Could you give some more details on what exactly it is you're looking for?

bizz84 commented 2 years ago

I have the same requirement so I can comment on this.

My app is configured to use multiple flavors.

As such, I have three sets of package_names / bundle IDs:

Here's how they look like on the Xcode build settings:

Screenshot 2021-12-13 at 09 40 02

And here's how they're set up inside android/app/build.gradle

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.codewithandrea.flutterfire_flavors"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    ...

    flavorDimensions "default"
    productFlavors { 
        production {
            dimension "default"
            applicationIdSuffix ""
            manifestPlaceholders = [appName: "Flutterfire Flavors"]
        }
        staging {
            dimension "default"
            applicationIdSuffix ".stg"
            manifestPlaceholders = [appName: "[STG] Flutterfire Flavors"]
        }        
        development {
            dimension "default"
            applicationIdSuffix ".dev"
            manifestPlaceholders = [appName: "[DEV] Flutterfire Flavors"]
        }
    }    

Accordingly, I want to have a separate Firebase project for each flavor.

I tried to run flutterfire configure on my project, but by default it only works with one Firebase project and doesn't take into account the platform-specific configuration.

Here's a log:

✔ Select a Firebase project to configure your Flutter application with · <create a new project> 
✔ Enter a project id for your new Firebase project (e.g. my-cool-project) · flutterfire-flavors-dev 
i New Firebase project flutterfire-flavors-dev created succesfully.
✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, web, macos 
i Firebase android app com.codewithandrea.flutterfire_flavors is not registered on Firebase project flutterfire-flavors-dev.
i Registered a new Firebase android app on Firebase project flutterfire-flavors-dev.

✔ Which ios bundle id do you want to use for this configuration, e.g. 'com.example.app'? · com.codewithandrea.flutterfire-flavors.dev 
i Firebase ios app com.codewithandrea.flutterfire-flavors.dev is not registered on Firebase project flutterfire-flavors-dev.
i Registered a new Firebase ios app on Firebase project flutterfire-flavors-dev.
✔ Which macos bundle id do you want to use for this configuration, e.g. 'com.example.app'? · com.codewithandrea.flutterfire-flavors 
i Firebase macos app com.codewithandrea.flutterfire-flavors is not registered on Firebase project flutterfire-flavors-dev.
i Registered a new Firebase macos app on Firebase project flutterfire-flavors-dev.
i Firebase web app flutterfire_flavors (web) is not registered on Firebase project flutterfire-flavors-dev.
i Registered a new Firebase web app on Firebase project flutterfire-flavors-dev.

Note how it detected com.codewithandrea.flutterfire_flavors as the Android app (non-flavored version) and it asked for the bundle ID on iOS and macOS.

How I would like this to work

Note: I use very_good_cli to automate the process of creating an app with multiple flavors, but the same applies if multiple flavors are created manually.

As it stands, the only reliable way of getting this to work is to:

It can be done, but it's error prone and the whole point of flutterfire is to automate all this configuration.

Any serious project would use multiple flavors, so I believe this is a very important feature to add.

Salakar commented 2 years ago

This is technically possible already with using the CLI flags, e.g.:

flutterfire config \
  --project=mycoolproject-dev \
  --out=lib/firebase_options_dev.dart \
  --ios-bundle-id=com.codewithandrea.flutterfire-flavors.dev \
  --macos-bundle-id=com.codewithandrea.flutterfire-flavors.dev \
  --android-app-id=com.codewithandrea.flutterfire-flavors.dev

So the options are already in place - albeit maybe a little manual

If someone would like to PR to make this less manual then please do :)

bizz84 commented 2 years ago

@Salakar that's great! That already takes much of the pain away as it needs to be run 3 times but at least it can be automated.

Salakar commented 2 years ago

I'm going to close this now since I think this is essentially covered now with the above flags and along with the recent addition of the --yes flag (to skip prompts and accept defaults). Meaning this can be automated, e.g. defining commands in melos scripts to switch environments

JCQuintas commented 2 years ago

@Salakar that is not a solution, as there is no documentation describing how to do that, nor it seems trivial.

Running flutterfire config also creates a firebase_app_id_file.json file that needs to exist in each of android and ios folder. This file doesn't seem to allow for flavors, so I can't have them scoped like firebase_app_id_file_development.json.

sarbogast commented 2 years ago

I agree with @JCQuintas. Plus those files were not generated by the first version of FlutterFire CLI, and when I delete google_services.json and the corresponding plugin configuration from android/app/build.gradle, and the firebase_app_id_file.json file from ios, everything seems to work fine without them. Why were those files added by default? What is their purpose? And if they are absolutely useful, you need to add command-line arguments to make it possible to override where these files will be generated so that they don't overwrite the last version when I run flutterfire configure for each of my flavor projects.

Salakar commented 2 years ago

Unfortunately these firebase_app_id_file.json files and .gradle file updates are required for native builds if you want certain plugins (like Analytics, Crashlytics & Performance) to function correctly on Android, iOS & macOS. Since these interact with native builds there isn't really a way to make them environment specific since the CLI would be unware of your Android/iOS build targets etc. Open to suggestions/pull requests on these.

Having said that, in the latest release I've just shipped two new flags (hidden), --no-apply-gradle-plugins and --no-app-id-json - which will disable these files being generated/changed should you need to opt-out, though I'd caution against doing so unless you know what you're doing with configuring the native parts of Android / iOS to work properly with the Firebase services mentioned above.

sarbogast commented 2 years ago

The first thing is to document why those files can be necessary. For example, in the CLI documentation, on the line about --[no-]apply-gradle-plugin there should be a mention explaining why you might not need to apply the gradle plugin and create the google-services.json file. There are a lot of projects that only use firebase core, auth and firestore, but not analytics, crashlytics and performance.

Second of all, there should be an equivalent option to this one for the app-id-json and that's great that you added that. Is it already in the current version, but just undocumented?

You could also make it so that flutterfire configure doesn't generate any of those files by default, and then add a command to flutterfire that adds those files for native plugins, something like flutterfire configure-native-plugins, but maybe that's overkill.

Then the most important thing is to make is possible to customize the path and the name of those files, just like -out lets us do it for firebase_options.dart. I use that already to have 3 versions of this file in my project, one for each environment/flavor, that I import into each of my main.darts. In Android, google-services-json could be stored in flavor-specific subdirectories (android/app/src/dev or android/app/src/prod instead of the default android/app) so that Gradle picks up on the right version depending on the flavor. So maybe a command-line parameter like -android-native-out that accepts a full path for google-services.json.

And finally, I would like a command line parameter to customize where the app id json file is generated for ios, path and file name as well, which would allow me to have different versions of that file for my different environments. And then because Firebase native libraries look for this file in a specific place with a specific name, I can have a script in my XCode build pipeline that picks up the proper version of the file and copies it where Firebase is expecting it to be. That's already what we did for flavor builds before. Agreed, you don't need to document that process in the CLI documentation, but giving that option makes it more flexible.

By the way, I'm preparing a step-by-step tutorial on how to flavorize a Flutter project including web support and firebase support, so if those options are added, I will also document them there.

sarbogast commented 2 years ago

By the way, I just tried to add the --no-apply-gradle-plugin flag, as described in the CLI documentation, but I got an error: Could not find an option named "no-apply-gradle-plugin"

And indeed, it is not documented in flutterfire help configure

Salakar commented 2 years ago

By the way, I just tried to add the --no-apply-gradle-plugin flag, as described in the CLI documentation, but I got an error: Could not find an option named "no-apply-gradle-plugin"

And indeed, it is not documented in flutterfire help configure

--no-apply-gradle-plugins (plural on plugins) there's an update for the docs waiting to fix the docs but we're in a docs change freeze right now sorry 🙈 it's a hidden flag so won't show on help commands currently but it is there - hidden until properly documented since we don't want anyone using it without knowing the full implications

Thanks for all the input previously also, I can certainly add options to change the output paths for both json files so let me look into that, the only caveat to this is that we need to note on the CLI that if you do this it won't work by default and you shouldn't do it unless you know what you're doing.

--no-app-id-json also exists in current version released yesterday, also hidden currently so won't show in --help. I wonder perhaps if these two flags should become one flag, something like --[no-]native-integration since effectively similar just ones for android and one ios.

petrnymsa commented 2 years ago

@Salakar In our app we have defined two flavors (ios schemes) but only one Firebase project. How can we achieve to have firebase_app_id_file.json per scheme?

One of the older project we have defined custom Build Phase which will copy correct GoogleServices.plist into project based on current scheme. Do we need something similar?

GitHelge commented 2 years ago

Hey guy, as long as the flutterfire_cli does not support multiple schemes for the firebase_app_id_file.json, can we keep using our old google-services.json and the GoogleService-Info-***.plist files with the new version of firebase_core (^1.16.0) and firebase_crashlytics (^2.7.2), or is it better to revert to previous versions?

enqvida commented 2 years ago

Don't know anyone who doesn't use flavors for the production app, so this issue should be a priority otherwise what's the point.

enqvida commented 2 years ago

@GitHelge Yes it works, if you set up Firebase the old way.

Ahmadre commented 2 years ago

For all who are searching for a solution (also working in pipelines):

# Install Firebase-CLI
curl -sL firebase.tools | bash

# Install FlutterFire CLI
flutter pub global activate flutterfire_cli

# Configure FlutterFire for ios, android and web
flutterfire configure -y --project MYAPP-$SCHEME --android-app-id=$BUNDLE_ID --ios-bundle-id=$BUNDLE_ID --macos-bundle-id=$BUNDLE_ID --platforms=android,ios,web

Edit: I suppose you to know how to handle flavors with flutter_flavorizr to use this solution!

djorgji commented 2 years ago

Flutter Flavors are a feature of flutter overall that the CLI is completely oblivious to at the moment. We are going to have to stay on an older version of firebase, but as customers of firebase this is a major blocker to being able to adopt some of the new features.

sarbogast commented 2 years ago

If you check out this article that I published recently, you will see that it's possible to work around it. The article is now also referenced in the documentation.

bounty1342 commented 2 years ago

@sarbogast this doesn't solve the issue with firebase_app_id_file.json file being not flavoured.

sarbogast commented 2 years ago

No but you only need this file in certain not so frequent situations. So at the very least for now you can disable its generation when you don't need it. Hopefully at some point we can customize its name and use a script to copy the right one. Or the libraries that need it for now won't need it anymore.

bounty1342 commented 2 years ago

I used https://medium.com/@animeshjain/build-flavors-in-flutter-android-and-ios-with-different-firebase-projects-per-flavor-27c5c5dac10b method to copy google-services.json with the old method. Maybe, something similar should work for the firebase_app_id_file.json.

Something like :

 environment="default"

 # Regex to extract the scheme name from the Build Configuration
 # We have named our Build Configurations as Debug-dev, Debug-prod etc.
 # Here, dev and prod are the scheme names. This kind of naming is required by Flutter for flavors to work.
 # We are using the $CONFIGURATION variable available in the XCode build environment to extract 
 # the environment (or flavor)
 # For eg.
 # If CONFIGURATION="Debug-prod", then environment will get set to "prod".
 if [[ $CONFIGURATION =~ -([^-]*)$ ]]; then
 environment=${BASH_REMATCH[1]}
 fi

 echo $environment

 # Name and path of the resource we're copying
 FIREBASEPPID_INFO_PLIST=firebase_app_id_file.json
 FIREBASEPPID_INFO_FILE=${PROJECT_DIR}/config/${environment}/${FIREBASEPPID_INFO_PLIST}

 # Make sure firebase_app_id_file.json exists
 echo "Looking for ${FIREBASEPPID_INFO_PLIST} in ${FIREBASEPPID_INFO_FILE}"
 if [ ! -f $FIREBASEPPID_INFO_FILE ]
 then
 echo "No firebase_app_id_file.json found. Please ensure it's in the proper directory."
 exit 1
 fi

 # Get a reference to the destination location for the firebase_app_id_file.json
 # This is the default location of firebase_app_id_file.json file
 PLIST_DESTINATION=${PROJECT_DIR}
 echo "Will copy ${FIREBASEPPID_INFO_PLIST} to final destination: ${PLIST_DESTINATION}"

 # Copy over the prod firebase_app_id_file.json for Release builds
 cp "${FIREBASEPPID_INFO_FILE}" "${PLIST_DESTINATION}"

Also instead of executed at the build phase, it should probably be place in the Pre-Action of the Scheme.

feinstein commented 2 years ago

@Salakar my app was configured last year to use FlutterFire, in a manual process. At each release I always take a look at the Quick Start guide, to see if I need to change some configuration, json, gradle, etc.

On the last release the docs were migrated and updated to only have a CLI option for the configurations, but my app has several whitelabel versions and stages, which I can't find a documentation on how to configure those.

I think there are 2 scenarios here:

  1. Multiple Apps on the same project. This will happen with whitelabel apps, or free and paid versions of the same app, where they have different package ids/bundle ids, but they all use the same crashlytics or remote config.

  2. Multiple Apps on different projects. This will happen with different stages of release (development, tests, production), so we could have a database segregation and not impact customers if we are playing with the database.

The CLI must support both of these scenarios, which can include flavors and dimensions on Android or Schemes and Targets on iOS. I can't see how could I configure 4 different Android package ids for the same project using the flags you provided before, as I have several flavors and dimensions.

For now I just updated the packages versions on pubspec.yaml, but since I can't run the CLI and have dart initialization, I imagine my Crashlytics reports won't be optimized for Flutter.

kinex commented 2 years ago

Unfortunately these firebase_app_id_file.json files and .gradle file updates are required for native builds if you want certain >plugins (like Analytics, Crashlytics & Performance) to function correctly on Android, iOS & macOS.

I am using Crashlytics (who Flutter/Firebase developer isn't?) so if I understood this correctly, I cannot remove google-services.json from my Android project after all. It is unclear can I still remove google-services related stuff from .gradle files as documented here. From the iOS project I can remove GoogleService-Info.plist, but I need to add a similar file firebase_app_id_file.json. So things do not get any simpler with iOS either especially with the flavors I have already configured years ago.

So I need google-services.json and similar config file for iOS as earlier, plus now there is also a new file firebase_options.dart which only contains duplicate information to what is already available in google-services.json and GoogleService-Info.plist (or firebase_app_id_file.json).

Maybe and hopefully this new command line tool helps new Flutter developers, but for me this seems only like a new problem and more work without any additional value.

UPDATE: It seems that GoogleService-Info.plist cannot be removed either. It is required by at least the plugin google_sign_in (there is no Firebase app without google_sign_in). Debug log also has other warnings from Firebase/Core and Firebase/Analytics complaining that GoogleService-Info.plist cannot be found. So I really don't understand what is this new documentation about "Dart-only initialization". It is not possible for a real world Flutter/Firebase app.

I summarized my findings and suggestions here: https://github.com/invertase/flutterfire_cli/issues/85

cedvdb commented 2 years ago

Just to resume a bit the discussion for a typical simple project with dev and prod (no staging here to make it simple) :

When running firebasecli configure:

The following command allows to create multiple versions of the json and plist:

flutterfire config \
  --project=mycoolproject-dev \
  --out=lib/firebase_options_dev.dart \
  --ios-bundle-id=com.mycoolproject.app.dev \
  --macos-bundle-id=com.mycoolproject.app.dev \
  --android-app-id=com.mycoolproject.app.dev

However this is necessary only for crashalytics and and analytics. In the event that someone only has dev and prod, where dev does not need crashalytics nor analytics, the following strategy can be used:

Is the above correct ?

feinstein commented 2 years ago

I am not sure how those commands will deal with flavors, since we need multiple json/plist in different native folders. Example: one json in the Android prod folder and another in the dev folder.

sarbogast commented 2 years ago

All that is needed if for the command to let us customize the path and/or name of those files so that we can then add our own scripts to copy the right version to the location and name expected by the native plugins, just like we used to do with legacy configuration.

cedvdb commented 2 years ago

Could a PR eventually land to remove the need for the json and plist files eventually ? Similar to this one https://github.com/flutter/plugins/pull/5250

neiljaywarner commented 2 years ago

@bizz84 what's your thoughts on this now?

sdstolworthy commented 2 years ago

For what it's worth, I resolved this issue by running flutterfire config multiple times. if you run flutterfire configure once for each flavor, and then move the resulting google-services.json into android/app/src/${flavorName}, then the build succeeds for each flavor. A big caveat here is that I have only tested this on Android so far, not on iOS. I haven't looked into what steps will be necessary to get the different flavors working on iOS with flutterfire.

acoutts commented 1 year ago

For android it does work like you said @sdstolworthy but for iOS the cli only outputs one file ios/firebase_app_id_file.json and it will overwrite it each time. We can manually fix it for iOS.

There is a warning printed when doing pod install:

Warning: firebase_app_id_file.json file does not exist. This may cause issues in upload-symbols. If this error is unexpected, try running flutterfire configure again.

I guess it can be ignored as long as you're sure you have set things up correctly.

What I did:

Future main() async { late final PackageInfo packageInfo; try { packageInfo = await PackageInfo.fromPlatform(); } catch (e, s) { /// Fallback value packageInfo = PackageInfo( appName: 'MyApp', packageName: 'com.myapp.prod', buildNumber: '0', version: '0.0.0', ); }

final isSandbox = packageInfo.packageName == 'com.myapp.sandbox';

await Firebase.initializeApp( options: isSandbox ? firebase_sandbox.DefaultFirebaseOptions.currentPlatform : firebase_prod.DefaultFirebaseOptions.currentPlatform, ); }


Here is my run script I used:
```sh
# Name of the resource we're selectively copying
FIREBASE_APP_ID_FILE=firebase_app_id_file.json

# Get references to sandbox and prod versions of firebase_app_id_file.json
# NOTE: These should only live on the file system and should NOT be part of the target (since we'll be adding them to the target manually)
FIREBASE_APP_ID_FILE_SANDBOX=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Sandbox/${FIREBASE_APP_ID_FILE}
FIREBASE_APP_ID_FILE_PROD=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Prod/${FIREBASE_APP_ID_FILE}

# Make sure the sandbox version of firebase_app_id_file.json exists
echo "Looking for ${FIREBASE_APP_ID_FILE} in ${FIREBASE_APP_ID_FILE_SANDBOX}"
if [ ! -f $FIREBASE_APP_ID_FILE_SANDBOX ]
then
    echo "No sandbox firebase_app_id_file.json found. Please ensure it's in the proper directory."
    exit 1
fi

# Make sure the prod version of firebase_app_id_file.json exists
echo "Looking for ${FIREBASE_APP_ID_FILE} in ${FIREBASE_APP_ID_FILE_PROD}"
if [ ! -f $FIREBASE_APP_ID_FILE_PROD ]
then
    echo "No prod firebase_app_id_file.json found. Please ensure it's in the proper directory."
    exit 1
fi

# Get a reference to the destination location for firebase_app_id_file.json
FILE_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
echo "Will copy ${FIREBASE_APP_ID_FILE} to final destination: ${FILE_DESTINATION}"

# Copy over the correct firebase_app_id_file.json for the current build configuration
if [ "${CONFIGURATION}" == "Debug-prod" ] || [ "${CONFIGURATION}" == "Release-prod" ] || [ "${CONFIGURATION}" == "Profile-prod" ]
then
    echo "Using ${FIREBASE_APP_ID_FILE_PROD}"
    cp "${FIREBASE_APP_ID_FILE_PROD}" "${FILE_DESTINATION}"
elif [ "${CONFIGURATION}" == "Debug-sandbox" ] || [ "${CONFIGURATION}" == "Release-sandbox" ] || [ "${CONFIGURATION}" == "Profile-sandbox" ]
then
    echo "Using ${FIREBASE_APP_ID_FILE_SANDBOX}"
    cp "${FIREBASE_APP_ID_FILE_SANDBOX}" "${FILE_DESTINATION}"
else
    echo "Error: invalid configuration specified: ${CONFIGURATION}"
fi

At build/run time, it all works as expected because there's only one ios/firebase_app_id_file.json file and it's in the correct spot at build time thanks to the run script. Depending which flavor I select, it copies the right one into place just like it does for the GoogleService-Info.plist file.

This is a tough thing to solve in the cli tool because flavors are by design such a flexibly-implemented thing - people use all sorts of different approaches. I think there might just need to be a good doc page outlining the approach for setting it up for flavors and it's up to the user to adapt it to their specific implementation.

MdeBruin93 commented 1 year ago

Hello,

One of the things I'm missing right here is the option to create a "secured" prod version of the firebase_options.dart file. I mean, I've managed to successfully setup a project using the flutterfire cli with the configure command. However, this just adds a dart file with my keys in plain text. This is obviously fine for local development, but not very recommended for the production build. I would love to have an option where the FirebaseOptions class can read the encrypted keys from a file .json or some other file (e.g. the google-services.json file that can be saved encrypted in a CI/CD tool).

nilsreichardt commented 1 year ago

HI @MdeBruin93, the google-service.json does not contain any confidential data. It's not a problem to commit this and publish it. Especially, for web apps it's super easy to access the google-service.json. So you don't need to worry about to make the google-service.json "secure". However, you set up your Firebase security rules and other server side protection to secure your data. See Firebase security checklist for more information.

ollyde commented 1 year ago

@russellwheatley great thanks; I hope we can implement live environmental switching; Having the recompile the app is either not an option (for QA teams) or a tremendous waste of time.

loolooii commented 1 year ago

@acoutts I'm running a copy script, basically, the same as I do with GoogleService-Info.plist. Can you tell me where in the build Phases you run this script? It seems that it's not there when Crashlytics script starts running.

acoutts commented 1 year ago

@loolooii my phases look like this in terms of ordering including my crashlytics one

Screen Shot 2022-08-01 at 9 04 12 AM
loolooii commented 1 year ago

@acoutts thanks man! It seems you do the copying way earlier, that must have been it. I ended up removing Crashlytics completely, it's nice and all, but I'll wait for it to be supported in the "new way". If not, I will try to do it again some time later. I needed success yesterday 😄

WalidChebaro commented 1 year ago

For android it does work like you said @sdstolworthy but for iOS the cli only outputs one file ios/firebase_app_id_file.json and it will overwrite it each time. We can manually fix it for iOS.

There is a warning printed when doing pod install:

Warning: firebase_app_id_file.json file does not exist. This may cause issues in upload-symbols. If this error is unexpected, try running flutterfire configure again.

I guess it can be ignored as long as you're sure you have set things up correctly.

What I did:

  • Ran the configure command multiple times, making sure to copy the ios/firebase_app_id_file.json file into my respective flavor folder each time. This is the same folder I store my GoogleService-Info.plist file for each flavor.

    • ios/Runner/Firebase/Prod/firebase_app_id_file.json
    • ios/Runner/Firebase/Sandbox/firebase_app_id_file.json
  • Added an xcode run script build phase which copies the correct firebase_app_id_file.json file into the root ios/ folder, just like the one for GoogleService-Info.plist works.
  • In my main.dart I use package_info_plus to know my package name to determine what flavor is running and configure firebase with the correct file accordingly:
import 'package:app/firebase_options_prod.dart';
import 'package:app/firebase_options_sandbox.dart';
import 'package:package_info_plus/package_info_plus.dart';

Future<void> main() async {
  late final PackageInfo packageInfo;
  try {
    packageInfo = await PackageInfo.fromPlatform();
  } catch (e, s) {
    /// Fallback value
    packageInfo = PackageInfo(
      appName: 'MyApp',
      packageName: 'com.myapp.prod',
      buildNumber: '0',
      version: '0.0.0',
    );
  }

  final isSandbox = packageInfo.packageName == 'com.myapp.sandbox';

  await Firebase.initializeApp(
  options: isSandbox
      ? firebase_sandbox.DefaultFirebaseOptions.currentPlatform
      : firebase_prod.DefaultFirebaseOptions.currentPlatform,
  );
}

Here is my run script I used:

# Name of the resource we're selectively copying
FIREBASE_APP_ID_FILE=firebase_app_id_file.json

# Get references to sandbox and prod versions of firebase_app_id_file.json
# NOTE: These should only live on the file system and should NOT be part of the target (since we'll be adding them to the target manually)
FIREBASE_APP_ID_FILE_SANDBOX=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Sandbox/${FIREBASE_APP_ID_FILE}
FIREBASE_APP_ID_FILE_PROD=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Prod/${FIREBASE_APP_ID_FILE}

# Make sure the sandbox version of firebase_app_id_file.json exists
echo "Looking for ${FIREBASE_APP_ID_FILE} in ${FIREBASE_APP_ID_FILE_SANDBOX}"
if [ ! -f $FIREBASE_APP_ID_FILE_SANDBOX ]
then
    echo "No sandbox firebase_app_id_file.json found. Please ensure it's in the proper directory."
    exit 1
fi

# Make sure the prod version of firebase_app_id_file.json exists
echo "Looking for ${FIREBASE_APP_ID_FILE} in ${FIREBASE_APP_ID_FILE_PROD}"
if [ ! -f $FIREBASE_APP_ID_FILE_PROD ]
then
    echo "No prod firebase_app_id_file.json found. Please ensure it's in the proper directory."
    exit 1
fi

# Get a reference to the destination location for firebase_app_id_file.json
FILE_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
echo "Will copy ${FIREBASE_APP_ID_FILE} to final destination: ${FILE_DESTINATION}"

# Copy over the correct firebase_app_id_file.json for the current build configuration
if [ "${CONFIGURATION}" == "Debug-prod" ] || [ "${CONFIGURATION}" == "Release-prod" ] || [ "${CONFIGURATION}" == "Profile-prod" ]
then
    echo "Using ${FIREBASE_APP_ID_FILE_PROD}"
    cp "${FIREBASE_APP_ID_FILE_PROD}" "${FILE_DESTINATION}"
elif [ "${CONFIGURATION}" == "Debug-sandbox" ] || [ "${CONFIGURATION}" == "Release-sandbox" ] || [ "${CONFIGURATION}" == "Profile-sandbox" ]
then
    echo "Using ${FIREBASE_APP_ID_FILE_SANDBOX}"
    cp "${FIREBASE_APP_ID_FILE_SANDBOX}" "${FILE_DESTINATION}"
else
    echo "Error: invalid configuration specified: ${CONFIGURATION}"
fi

At build/run time, it all works as expected because there's only one ios/firebase_app_id_file.json file and it's in the correct spot at build time thanks to the run script. Depending which flavor I select, it copies the right one into place just like it does for the GoogleService-Info.plist file.

This is a tough thing to solve in the cli tool because flavors are by design such a flexibly-implemented thing - people use all sorts of different approaches. I think there might just need to be a good doc page outlining the approach for setting it up for flavors and it's up to the user to adapt it to their specific implementation.

this would be a better script:

environment="default"

# Regex to extract the scheme name from the Build Configuration
# We have named our Build Configurations as Debug-dev, Debug-prod etc.
# Here, dev and prod are the scheme names. This kind of naming is required by Flutter for flavors to work.
# We are using the $CONFIGURATION variable available in the XCode build environment to extract 
# the environment (or flavor)
# For eg.
# If CONFIGURATION="Debug-prod", then environment will get set to "prod".
if [[ $CONFIGURATION =~ -([^-]*)$ ]]; then
environment=${BASH_REMATCH[1]}
fi

echo $environment

# Name and path of the resource we're copying
FIREBASE_APP_ID_JSON=firebase_app_id_file.json
FIREBASE_APP_ID_FILE=${PROJECT_DIR}/Config/${environment}/${FIREBASE_APP_ID_JSON}

GOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist
GOOGLESERVICE_INFO_FILE=${PROJECT_DIR}/Config/${environment}/${GOOGLESERVICE_INFO_PLIST}

# Make sure GoogleService-Info.plist exists
echo "Looking for ${GOOGLESERVICE_INFO_PLIST} in ${GOOGLESERVICE_INFO_FILE}"
if [ ! -f $GOOGLESERVICE_INFO_FILE ]
then
echo "No GoogleService-Info.plist found. Please ensure it's in the proper directory."
exit 1
fi

# Make sure firebase_app_id_file.json exists
echo "Looking for ${FIREBASE_APP_ID_JSON} in ${FIREBASE_APP_ID_FILE}"
if [ ! -f $FIREBASE_APP_ID_FILE ]
then
echo "No firebase_app_id_file.json found. Please ensure it's in the proper directory."
exit 1
fi

FIREBASE_PLIST_JSON_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
# Get a reference to the destination location for the GoogleService-Info.plist
# This is the default location where Firebase init code expects to find GoogleServices-Info.plist file
echo "Will copy ${GOOGLESERVICE_INFO_PLIST} to final destination: ${FIREBASE_PLIST_JSON_DESTINATION}"

# Get a reference to the destination location for the firebase_app_id_file.json
# This is the default location where Firebase init code expects to find firebase_app_id_file.json file
echo "Will copy ${FIREBASE_APP_ID_JSON} to final destination: ${FIREBASE_PLIST_JSON_DESTINATION}"

# Copy over the prod GoogleService-Info.plist for Release builds
cp "${GOOGLESERVICE_INFO_FILE}" "${FIREBASE_PLIST_JSON_DESTINATION}"

# Copy over the prod firebase_app_id_file.json for Release builds
cp "${FIREBASE_APP_ID_FILE}" "${FIREBASE_PLIST_JSON_DESTINATION}"
diegopq commented 1 year ago

any update to configure the flavors using the cli directly?

russellwheatley commented 1 year ago

Hey folks, we're looking to update the CLI to help users who require multiple environment setup. The biggest concern for us regards the different approaches to setting up build types/flavors/schemes. We don't want to make assumptions about your particular setup. We propose exposing 3 new options which allow you to choose where to write the service file (“Google-Service-Info.plist” for iOS/macOS & “google-services.json” for Android). So for instance, you could write:

flutterfire configure \
  --project=mycoolproject-dev \
  --out=lib/firebase_options_dev.dart \
  --ios-bundle-id=com.codewithandrea.flutterfire-flavors.dev \
  --macos-bundle-id=com.codewithandrea.flutterfire-flavors.dev \
  --android-app-id=com.codewithandrea.flutterfire-flavors.dev \
  --ios-out=/ios/directory/Google-Service-Info.plist \
  --android-out=/android/directory/google-services.json \
  --macos-out=/macos/directory/Google-Service-Info.plist \

This allows you to choose the path and the name of the file. According to the documentation for setting up multi-projects for android, it seems fairly straight forward configuring different environments. You just write the file to the relevant build type directory:

app/
    google-services.json
    src/development/google-services.json
    src/release/google-services.json
    ...

iOS is more involved, Firebase recommends two methods in the the documentation:

Either:

  1. Write the service files, renaming and selecting the different config file for each target (This isn't a viable option if users only have one target).
  2. The other method is to load the plist file at runtime which isn't a viable option as far as I can tell.

There is also a third solution that would require having a build script to inject the correct Google-Service-Info.plist. This would be a better way if you only use one target in your project.

Let me know what you think. Thanks.

feinstein commented 1 year ago

Let me see if we are in the same page.

If I have a white label app, that turns out to be 2 different apps in production, with 2 different Android ids, I will have to run the CLI once for each of my Android apps, and the last time I run the CLI, it won't interfere with the previous times I have run it, and configured the other Android apps?

I want to be sure running the CLI multiple times, for different Android apps, won't mess up any previous configuration of other apps, since I have 3 android and 3 ios apps, generated from the same source code, just changing the app's IDs, names and icons.

erf commented 1 year ago

Hey folks, we're looking to update the CLI to help users who require multiple environment setup. The biggest concern for us, regards the different approaches to setting up build types/flavors/schemes. We don't want to make assumptions about your particular setup. We propose exposing 3 new options which allow you to choose where to write the service file (“Google-Service-Info.plist” for iOS/macOS & “google-services.json” for Android). So for instance, you could write:

flutterfire config \
  --project=mycoolproject-dev \
  --out=lib/firebase_options_dev.dart \
  --ios-bundle-id=com.codewithandrea.flutterfire-flavors.dev \
  --macos-bundle-id=com.codewithandrea.flutterfire-flavors.dev \
  --android-app-id=com.codewithandrea.flutterfire-flavors.dev \
  --ios-out=/ios/directory/Google-Service-Info.plist \
  --android-out=/android/directory/google-services.json \
  --macos-out=/macos/directory/Google-Service-Info.plist \

Do we still need the Google-Service-Info.plist and google-services.json file when we have the firebase_options_dev.dart?

ollyde commented 1 year ago

Just to note; many of us switch on the login screen via a few different environments; real time. having to use different builds is very painful for QA.

kinex commented 1 year ago

Hey folks, we're looking to update the CLI to help users who require multiple environment setup. The biggest concern for us, regards the different approaches to setting up build types/flavors/schemes. We don't want to make assumptions about your particular setup. We propose exposing 3 new options which allow you to choose where to write the service file (“Google-Service-Info.plist” for iOS/macOS & “google-services.json” for Android). So for instance, you could write:

flutterfire config \
  --project=mycoolproject-dev \
  --out=lib/firebase_options_dev.dart \
  --ios-bundle-id=com.codewithandrea.flutterfire-flavors.dev \
  --macos-bundle-id=com.codewithandrea.flutterfire-flavors.dev \
  --android-app-id=com.codewithandrea.flutterfire-flavors.dev \
  --ios-out=/ios/directory/Google-Service-Info.plist \
  --android-out=/android/directory/google-services.json \
  --macos-out=/macos/directory/Google-Service-Info.plist \

The most important option would be that this tool does not write the service files at all and most importantly does not access my Firebase projects in the cloud in any way (for security reasons). I am developing my app actively and publishing updates almost monthly. I have updated the service files total of two times during the lifetime of my app and the last time I updated them was in June 2019. So it really is not a problem to download and update those files manually when needed as I have already done last 3 years. I don't have anything against automation, but in my opinion this is not a place where automation is really needed especially because it only causes security concerns. Well, I don't know if this tool can do anything useful if it cannot access the Firebase projects in cloud, but that's why I suggested to Add documentation how to use FlutterFire packages the old way without this tool (and keep the documentation up to date) which is still my preferred option.

russellwheatley commented 1 year ago

Let me see if we are in the same page.

If I have a white label app, that turns out to be 2 different apps in production, with 2 different Android ids, I will have to run >the CLI once for each of my Android apps, and the last time I run the CLI, it won't interfere with the previous times I have >run it, and configured the other Android apps?

I want to be sure running the CLI multiple times, for different Android apps, won't mess up any previous configuration of other apps, since I have 3 android and 3 ios apps, generated from the same source code, just changing the app's IDs, names and icons.

Hey @feinstein, the idea is that it will grab the config from the Firebase Server and write to where ever you want it in your project setup with whatever file name you choose. They are independent file writes and won't interfere with each other. The setup part depends on how you've set it up. Firebase has its recommended way of setup for android flavors, not sure how else to set it up other than that method if that answers your question.

russellwheatley commented 1 year ago

Do we still need the Google-Service-Info.plist and google-services.json file when we have the firebase_options_dev.dart?

Hey @erf, The service file is used before dart is initialized on native apps, therefore, the Dart config initialization code is ignored as default app has already been setup. We were thinking of removing android, iOS & macOS dart initialisation code from flutterfire configure, but some users use it for second app initialization as you can change certain aspects like RTDB & Auth URL to a different address in your Firebase project.

russellwheatley commented 1 year ago

Hey @kinex, you can find manual installation here. You don't have to use flutterfire configure.

erf commented 1 year ago

Do we still need the Google-Service-Info.plist and google-services.json file when we have the firebase_options_dev.dart?

Hey @erf, The service file is used before dart is initialized on native apps, therefore, the Dart config initialization code is ignored as default app has already been setup. We were thinking of removing android, iOS & macOS dart initialisation code from flutterfire configure, but some users use it for second app initialization as you can change certain aspects like RTDB & Auth URL to a different address in your Firebase project.

Hey @russellwheatley i thought the Dart initialization would take over for the service file..? I liked the idea of adding multiple targets using pure Dart via --target=lib/main_dev.dart and --target=lib/main_prod.dart, which would load the appropriate firebase_options_{prod|dev}.dart file. So much easier than using multiple schemes.

russellwheatley commented 1 year ago

We wanted to move towards dart initialization, @erf. But there are too many unforeseen issues. Ultimately, we don't control how each Firebase product uses the service file, and it appears they are needed in certain circumstances.

erf commented 1 year ago

@russellwheatley so the call to Firebase.initializeApp given custom options does not have any effect now? Will that argument be removed in the future?

feinstein commented 1 year ago

Hey @kinex, you can find manual installation here. You don't have to use flutterfire configure.

The website has lots of warnings at the top, saying the content is not guaranteed to be updated and the manual configuration is not recommended, so when I read this, I get full of FUD that my project will be unstable.