Skip to content

Instantly share code, notes, and snippets.

@cohenadair
Last active September 26, 2023 05:39
Show Gist options
  • Save cohenadair/3a2aff5084603bfa65824f09cf74206e to your computer and use it in GitHub Desktop.
Save cohenadair/3a2aff5084603bfa65824f09cf74206e to your computer and use it in GitHub Desktop.
Setting up development and production Firebase environments for iOS

Firebase Environments

Last updated: October 21st, 2019.

At the time of writing this gist (January 4th, 2017), I was unable to find true sandboxing to separate development and production environments for a Firebase project. The closest we can get is to create two separate Firebase projects -- one for development and one for production.

Pros

  • Complete separation and isolation of all Firebase features.
  • Freedom to experiment without risking the corruption of production data.

Cons

  • There is no way to copy production data to your development project (that I am aware of).
  • Any settings changes made to your development project also needs to be manually applied to your production project.

Setup

This method will not work correctly with Crashlyitcs. Crashlytics has "GoogleService-Info.plist" hardcoded somewhere, so renaming the file will not work. Instead, consider setting up multiple directories, one for each Firebase project, and use a build script to move the Firebase configuration file appropriately. See this comment for more details.

  1. Go to the Firebase console.

  2. Create two projects. That's Projects, not Apps. Apps within a project share certain features, including the Realtime Database. Make sure both projects have a Bundle ID matching your Xcode project's Bundle ID.

  3. In each project, create an iOS App, and each GoogleServices-Info.plist file to your Xcode project.

  4. Rename one of the plist files to clearly identify it as the development (or production) configuration, such as GoogleServices-Info-Dev.plist.

  5. Configure Firebase with the following code:

    Swift

    #if DEBUG
        let firebaseConfig = Bundle.main.path(forResource: "GoogleService-Info-Dev", ofType: "plist")
    #else
        let firebaseConfig = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist")
    #endif
    
    guard let options = FIROptions(contentsOfFile: firebaseConfig) else {
        fatalError("Invalid Firebase configuration file.")
    }
    
    FIRApp.configure(with: options)

    Objective-C

    #if DEBUG
        NSString *firebaseConfig = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info-Dev" ofType:@"plist"];
    #else
        NSString *firebaseConfig = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
    #endif
    
    FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:firebaseConfig];
    if (options == nil) {
        // Invalid Firebase configuration file.
        return;
    }
    
    [FIRApp configureWithOptions:options];

Now, when your app is run from Xcode, your development Firebase project will be used.

Note that if you are using FirebaseAuth, you will need to setup each authentication method (i.e. Google, Facebook) for both Firebase projects.

Credits

Conclusion

If you know of a better method, please let me know. This isn't ideal, but I think it works well for what we're given.

@jjdp
Copy link

jjdp commented Mar 3, 2017

what would be the equivalent for step 5 in objective c?

@cohenadair
Copy link
Author

cohenadair commented Apr 21, 2017

@jjdp Sorry, I just saw this now, and I imagine you've solved your issue. Regardless, here's what I think the Objective-C equivalent would be (beware, this is untested).

#if DEBUG
    NSString *firebaseConfig = [[NSBundle mainBundle] pathForResource:"GoogleService-Info-Dev" ofType:"plist"];
#else
    NSString *firebaseConfig = [[NSBundle mainBundle] pathForResource:"GoogleService-Info" ofType:"plist"];
#endif

FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:firebaseConfig];
if (options == nil) {
    assert(false, "Invalid Firebase configuration file.");
}
[FIRApp configureWithOptions:options];

E: Ah, I see you have solved it in your fork. :)

@balrajOla
Copy link

@cohenadair How can i reconfigure Firebase if I change the App environment at runtime. Is there a way?

@AstmDesign
Copy link

How I can use the same scenario in Android ?!
I need to separate the development and the production environments for a Firebase project on Android

@cohenadair
Copy link
Author

Sorry guys, GitHub really needs to implement Gist notifications.

@balrajOla The Firebase documentation recommends configuring Firebase in the application:didFinishLaunchingWithOptions , but there is nothing that says it can't be configured again during runtime. If this is possible, the process would be the same. Instead of using the #if DEBUG directive, you would use your runtime equivalent variable to determine which service file to use, and then call FIRApp.configure again.

Source: https://firebase.google.com/docs/ios/setup

@AstmDesign I don't know Android as well, but a quick search shows that it can be done using Gradle configuration:
https://stackoverflow.com/questions/30772201/google-services-json-for-different-productflavors

@aabanaag
Copy link

aabanaag commented Mar 4, 2019

Thanks for this gist! Anyways just would like to ask if its generally safe to include googleserviceinfo into your git repository?

@cohenadair
Copy link
Author

cohenadair commented Mar 4, 2019

If your Firebase database is configured with the correct permissions, it should be fine.

This reddit thread has some good information: https://www.reddit.com/r/iOSProgramming/comments/5pluix/is_it_ok_to_upload_firebases/

This SO answer is also helpful: https://stackoverflow.com/questions/44937175/firebase-should-i-add-googleservice-info-plist-to-gitignore

@or-else
Copy link

or-else commented Oct 21, 2019

Does not work with Crashlytics.

@cohenadair
Copy link
Author

Does not work with Crashlytics.

I'm not familiar with how Crashlytics is set up. When this post was written, Crashlyics was still its own entity, separate from Firebase. It's possible there are other setup requirements in addition to the normal Firebase plist file.

Perhaps if you elaborated a bit we'd be able to troubleshoot.

@or-else
Copy link

or-else commented Oct 21, 2019

Essentially Crashlytics has GoogleServices-Info.plist hardcoded somewhere. It fails to build if the file with this name is not found. The fix is to use a bash script to copy appropriate GoogleServices-Info.plist to the destination. Something like:

#!/bin/bash

# This script selects appropriate GoogleService-Info.plist for the given build configuration.

# Names of source resource files
GOOG_DEV=${PROJECT_DIR}/${TARGET_NAME}/GoogleService-Info-Development.plist
GOOG_PROD=${PROJECT_DIR}/${TARGET_NAME}/GoogleService-Info-Production.plist

# Destination location for the resource. Also rename it to GoogleService-Info.plist
GOOG_DST=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist

# Select appropriate GoogleService-Info-ABC123.plist for the current build.
if [ "${CONFIGURATION}" == "Release" ]; then
  GOOG_SRC="${GOOG_PROD}"
else
  GOOG_SRC="${GOOG_DEV}"
fi

if [ ! -f "$GOOG_SRC" ]; then
  echo "Missing 'GoogleService-Info.plist' source at '${GOOG_SRC}"
  exit 1
fi

cp "${GOOG_SRC}" "${GOOG_DST}"

@cohenadair
Copy link
Author

Oh interesting. Thanks for the snippet. I'll add a link to the post.

I think I had to use a similar build script for Flutter and Firebase.

@wkoutre
Copy link

wkoutre commented Nov 8, 2019

@or-else Thank you!

@VisetDev
Copy link

@cohenadair @wkoutre Also firebase authentication will fail if GoogleServices.json is not found or miss proper environment configuration (It should match the running project).

@VisetDev
Copy link

This is an old post but I find it useful nowadays.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment