193

I am using a build scheme for prod and one for staging (with 2 different bundle identifiers) and I am trying to use a separate GoogleService-Info.plist for each scheme. Is there any way to manually select the plist file to use when initialising GCM (and goole login)? Or is its possible to avoid using the plist and do the setup manually?

Thanks!

nwaxgui
  • 1,931
  • 2
  • 10
  • 3

25 Answers25

233

Details

Tested on:

  • Xcode 9.2
  • Xcode 10.2 (10E125)
  • Xcode 11.0 (11A420a)

Solution

  1. Create folder with all your Google.plist files (with different names) in project

enter image description here

  1. Add run script

enter image description here

Do not forget to change PATH_TO_GOOGLE_PLISTS value

Code

PATH_TO_GOOGLE_PLISTS="${PROJECT_DIR}/SM2/Application/Firebase"

case "${CONFIGURATION}" in

   "Debug_Staging" | "AdHoc_Staging" )
        cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-dev.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

   "Debug_Production" | "AdHoc_Production" | "Distribution" | "Test_Production" )
        cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-prod.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

    *)
        ;;
esac

Build schemes names

enter image description here

dsgriffin
  • 66,495
  • 17
  • 137
  • 137
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
  • 3
    This is the right answer. Apparently Firebase Analytics requires that plist file in your app's root directory, even if you call `configure(options:)`. https://github.com/firebase/quickstart-ios/issues/5 – Rob Bajorek Jun 07 '18 at 18:25
  • 2
    This is a very nice solution and should be accepted as the answer. – Luke Brandon Farrell Sep 28 '18 at 11:51
  • Just wondering if this should be cp -r or just cp? Is it a directory? – SmileBot Feb 19 '19 at 20:40
  • 2
    @smileBot you may skip `-r`, more info: [cp command in Linux/Unix](https://www.rapidtables.com/code/linux/cp.html) – Vasily Bodnarchuk Feb 19 '19 at 20:46
  • 2
    `case "${CONFIGURATION}" in "Debug" ) cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Staging-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; "Release" ) cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; *) cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Staging-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; esac` I did this but it always taking GoogleService-Staging-Info.plist in both mode (Debug & Release). Anything wrong here?? – Bhavin_m Apr 24 '19 at 07:03
  • where did you put the run script? I have mine put before Copy Bundle Resources but it gives me an error `error: Build input file cannot be found: '/GoogleService-Info.plist'` – mikey May 01 '19 at 05:03
  • @mikey I placed this code before Copy Bundle Resources. Did you add base googleservice-info.plist to your project? This script will replace already added googleservice-info.plist. – Vasily Bodnarchuk May 02 '19 at 10:56
  • 1
    Thank you @VasilyBodnarchuk, I think the problem I had, is that I didn't add googleservice-info to my project, and also I had another script deleting the file at the end of the build phase (I wanted to make sure to get a fresh copy on every build). I ended up doing this `cp -v "sorcerers-gold/Config/GoogleService-Info-"$FIREBASE_ENVIRONMENT".plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"` and having using the FIREBASE_ENVIRONMENT to decide which file to pick up – mikey May 02 '19 at 23:49
  • Can I use two Firebase projects from one xcode project simultaneously? Not as Debug & Release? Cause when I try to do this I always end up with already configured crash. I have followed the latest instruction from Firebase official document. I am still searching for how can I configure two separate Firebase Project at the same time. Or how could I unconfigure one and then configure another one? Thanks – Tulon Oct 08 '19 at 10:01
  • @Tulon I think, it is possible to keep two plists in a project. But, you will have to switch configuration of the firebase in project, because you can’t setup two firebases in the app. https://stackoverflow.com/a/52964211 – Vasily Bodnarchuk Oct 08 '19 at 21:27
  • @VasilyBodnarchuk, Actually I did. Just configured two of them in `didFinishLaunchingWithOptions` one after another by following Firebase's official documentation. The main thing is I had to call both `configue` func only once in the life cycle of that app. Prior I was trying to call them in `viewDidload` of a certain `viewController`. So it was giving objection. Thanks – Tulon Oct 09 '19 at 16:27
  • 1
    I didn't find the 'Application' folder and my schemes names were standard, so I had to adjust the script to make it work: I placed the Firebase folder inside YourAppName and used "Debug" and "Release" as scheme names: `PATH_TO_GOOGLE_PLISTS="${PROJECT_DIR}/YourAppName/Firebase" case "${CONFIGURATION}" in "Debug" ) cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-dev.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; "Release" ) cp -r " ... ` – Raphael Pinel Mar 20 '20 at 08:48
  • Right now I m getting error. Please help me to resolve this. GOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist GOOGLESERVICE_INFO_DEV=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Development/${GOOGLESERVICE_INFO_PLIST} case "${CONFIGURATION}" in "Debug (Development)" | "Release (Development)" ) cp “${GOOGLESERVICE_INFO_DEV}” “${PLIST_DESTINATION}” ;; *) ;; esac – Himani Sharma May 06 '20 at 08:07
  • @VasilyBodnarchuk Yes provided. But still not getting crash logs on firebase. – Himani Sharma May 06 '20 at 12:10
  • @VasilyBodnarchuk GOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist GOOGLESERVICE_INFO_DEV=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Development/${GOOGLESERVICE_INFO_PLIST} GOOGLESERVICE_INFO_QAT=${PROJECT_DIR}/${TARGET_NAME}/Firebase/QAT/${GOOGLESERVICE_INFO_PLIST} case "${CONFIGURATION}" in "Debug (Dev)" | "Release (Dev)" ) cp -r "${GOOGLESERVICE_INFO_DEV}" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; "Debug (QAT)" | "Release (QAT)" ) cp -r "${GOOGLESERVICE_INFO_QAT}" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; *) ;; esac – Himani Sharma May 06 '20 at 12:11
  • @HimaniSharma ok. Can you compile and run your project? If no, what error Xcode sends to you? If yes, run your project in simulator and find all "firebase" in logs. You can try to understand what is going on. P.S. also update firebase pod. – Vasily Bodnarchuk May 06 '20 at 13:01
  • Thanks. I spent one and half day on this issue. – venky Jun 13 '20 at 19:17
  • @VasilyBodnarchuk i have added the script, given the appropriate path, added the script before copy bundle resources but it crashes with the error 'FirebaseApp.configure()` in Swift) could not find a valid GoogleService-Info.plist in your project. – Usama bin Attique Jul 20 '20 at 12:02
  • @UsamabinAttique do you need the sample of this project? – Vasily Bodnarchuk Jul 23 '20 at 15:44
  • 1
    I had an issue where when adding the two plist to the project it has also added to copy resource build phase as well, which resulted in `Multiple commands produce ...plist` error to overcome this I had to remove the plist from copy resource build pace. more info can be found in this SO post https://stackoverflow.com/questions/50718018/xcode-10-error-multiple-commands-produce – Ismail Iqbal Nov 22 '20 at 09:37
  • Excellent. I turned this into a one-liner by adding a user-defined build setting called `GOOGLE_SERVICE_INFO_PLIST` and setting it to the path I wanted to use for each configuration. Then the script is just `cp "${GOOGLE_SERVICE_INFO_PLIST}" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"`. – McMath Jan 01 '22 at 08:01
  • This solution worked great for a flutter / dart application since most flutter apps have multiple schemes and one single target. – Nick N Jul 08 '23 at 17:41
93

@inidona 's answer worked for me. After I converted it to Swift

for Swift 2.3:

let filePath = NSBundle.mainBundle().pathForResource("GoogleService-Info", ofType: "plist")
let options = FIROptions(contentsOfFile: filePath)
FIRApp.configureWithOptions(options)

for Swift 3.0:

let filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist")!
let options = FIROptions(contentsOfFile: filePath)
FIRApp.configure(with: options)

for Swift 4.0:

let filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist")!
let options = FirebaseOptions(contentsOfFile: filePath)
FirebaseApp.configure(options: options!)
CristinaTheDev
  • 1,952
  • 2
  • 17
  • 25
Essam Elmasry
  • 1,212
  • 11
  • 11
  • 2
    With the above code, do you have two different files `GoogleService-Info.plist` files at different locations or may two files with different names. Could you please provide some more information on what are the actual file names and where are they placed? – Varun Gupta Oct 20 '16 at 06:02
  • 1
    I have 2 files named GoogleService-Info-dev.plist GoogleService-Info-live.plist this code allows you to tell ur app whick info file you want to work with instead of the default GoogleService-Info-dev.plist use conditional statement or target flags to alternate between your files – Essam Elmasry Oct 20 '16 at 14:36
  • yup, in the AppDelegate – Essam Elmasry Oct 24 '18 at 12:56
  • 2
    gives me 'Could not locate configuration file: 'GoogleService-Info.plist'' – orium Jan 18 '19 at 14:58
  • 6
    Recent document says "Warning: This approach can impact Analytics collection in some circumstances" https://firebase.google.com/docs/projects/multiprojects – Shingo Fukuyama May 08 '20 at 00:49
  • And in the same document they say it's "If the builds are part of a single target, the best option" – Timur Suleimanov Nov 05 '20 at 14:53
  • This is not the recommended approach! You WILL miss any metric, crash, or analytic that needs uploaded from the point your app launches to the point your app initializes the .plist. ONLY do this if you are not uploading any metric or log before initialization (e.g. Crashalytics) – Erik Bean Jul 25 '22 at 13:22
47

If the GoogleService-Info.plist has a different name it will affect your analytics results. Firebase will warn you about this. For this reason, none of these runtime-solutions will provide the best analytics results.

There are two solutions that won't mess with Analytics.

  1. Use a different target with each scheme and associate each version of GoogleService-Info.plist with its own target. See Target Membership in the File inspector on the right hand side in Xcode. For further info See this question.

  2. Use a build phase script to copy the correct version of GoogleService-Info.plist into the build directory. I use a different bundle ID for staging and production. This enables me to have both versions of the app installed in parallel. It also means with the script below I can name my different GoogleService-Info.plist files with the bundle ID. For example:

  • GoogleService-Info-com.example.app.plist
  • GoogleService-Info-com.example.app.staging.plist

Build Phase Script

PATH_TO_CONFIG=$SRCROOT/Config/GoogleService-Info-$PRODUCT_BUNDLE_IDENTIFIER.plist
FILENAME_IN_BUNDLE=GoogleService-Info.plist
BUILD_APP_DIR=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
echo cp $PATH_TO_CONFIG "$BUILD_APP_DIR/$FILENAME_IN_BUNDLE"
cp $PATH_TO_CONFIG "$BUILD_APP_DIR/$FILENAME_IN_BUNDLE"

Note: You will have to change PATH_TO_CONFIG to suit you setup.

Build Phase Script

Onato
  • 9,916
  • 5
  • 46
  • 54
  • 2
    This is the best solution so far. Because Firebase Crashlytics can only use GoogleService-Info.plist file to upload dSYM files using 'upload-symbols' script - this solution works perfectly! – Alex Aug 02 '20 at 13:22
  • 1
    I confirm this solution also works with Firebase Crashlytics installed through Swift Package Manager. – Meng-Yuan Huang Feb 14 '22 at 09:05
  • @onato I've tried a similar way but, after adding crashlytics. It's not working. I got the below error. ```Error (Xcode): Could not get GOOGLE_APP_ID in Google Services file from build environment``` – Parth Bhanderi Sep 12 '22 at 11:00
37

Check this article: https://medium.com/@brunolemos/how-to-setup-a-different-firebase-project-for-debug-and-release-environments-157b40512164

On Xcode, create two directories inside your project: Debug and Release. Put each GoogleService-Info.plist file there.

On AppDelegate.m, inside the didFinishLaunchingWithOptions method, put the code:

Objective-C

  NSString *filePath;
#ifdef DEBUG
  NSLog(@"[FIREBASE] Development mode.");
  filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist" inDirectory:@"Debug"];
#else
  NSLog(@"[FIREBASE] Production mode.");
  filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist" inDirectory:@"Release"];
#endif

  FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
  [FIRApp configureWithOptions:options];

Swift 4

var filePath:String!
#if DEBUG
    print("[FIREBASE] Development mode.")
    filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist", inDirectory: "Debug")
#else
    print("[FIREBASE] Production mode.")
    filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist", inDirectory: "Release")
#endif

let options = FirebaseOptions.init(contentsOfFile: filePath)!
FirebaseApp.configure(options: options)

Drag & drop both Debug and Release folders to the Build Phases > Copy Bundle Resources:

Build Phases > Copy Bundle Resources

That's it :)

Bruno Lemos
  • 8,847
  • 5
  • 40
  • 51
  • 3
    This worked for me. You have to add these folders there as reference, otherwise it will crash, indeed. Thanks! – Eironeia Feb 13 '18 at 01:42
  • 1
    This solution still doesn't solve the issue for `Analytics` framework, you cannot tell which `.plist` is being loaded. – Lifely Nov 20 '18 at 10:38
  • @Bruno Lemos, Can I use two Firebase projects from one xcode project simultaneously? Not as `Debug` & `Release`? Cause when I try to do this I always end up with `already configured crash.` I have followed the latest instruction from Firebase official document. Thanks – Tulon Oct 08 '19 at 09:57
28

I think you can use this way to configure your GoogleService-Info.plist dynamicly and use different names for different bundle identifiers.

ciao Andreas

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
[FIRApp configureWithOptions:options];
inidona
  • 288
  • 3
  • 5
  • 6
    With the above code, do you have two different files GoogleService-Info.plist files at different locations or may two files with different names. Could you please provide some more information on what are the actual file names and where are they placed? – Varun Gupta Oct 20 '16 at 06:03
  • 4
    I am gettin this error in log during configureWithOptions call: `Could not locate configuration file: 'GoogleService-Info.plist'` – Babken Vardanyan Apr 16 '18 at 15:39
26

This answer is very much inspired by @abbood's answer, but a bit more specific on how to do it.

For each of your targets, e.g. dev, stg, prod:

  • Download the corresponding GoogleService-Info.plist to a separate folder named after your target
  • In Xcode, right-click your app folder and choose Add files to "your app" enter image description here
  • Select the folder containing the target's GoogleService-Info.plist, make sure Copy items if needed and Create groups are selected, check only the corresponding target in the list of targets, and press Add enter image description here

That's it. Now you should have something similar to this structure

enter image description here

When you build a target, the correct GoogleService-Info.plist will be used.

Bjørn Egil
  • 2,398
  • 1
  • 20
  • 22
22

I noticed that google expects the filename to be GoogleServiceInfo.plist in the code:

 * The method |configureWithError:| will read from the file GoogleServices-Info.plist bundled with
 * your app target for the keys to configure each individual API. To generate your
 * GoogleServices-Info.plist, please go to https://developers.google.com/mobile/add
 *
 * @see GGLContext (Analytics)
 * @see GGLContext (SignIn)
 */
@interface GGLContext : NSObject

the key phrase is this one

read from the file GoogleServices-Info.plist bundled with your app target

So I simply copied the same file and put it into different directories, and bounded it to different targets:

enter image description here

abbood
  • 23,101
  • 16
  • 132
  • 246
20

Late but I think I must post this answer to help new developers, I found a very good article that resole my problem and I promise it can help you as well :)
Check this article that resolve your problem as well.

Step 1:
Copy the GoogleService-Info.plist corresponding to your Firebase development environment into the Dev directory. Similarly, copy the GoogleService-Info.plist corresponding to your Firebase production environment in the Prod directory. Make sure to uncheck “Copy items if needed” and all targets under “Add to targets”.

(Step 1 image link (I do not able to add image because of less reputations))

Step 2:
In the Xcode project navigator, select the app target. Switch to the Build Phases tab at the top, then add a New Run Script Phase. Name the phase “Setup Firebase Environment GoogleService-Info.plist”, or something to that effect, and place it before the “Copy Bundle Resources” step.

Step 3:
Implement a shell script that will copy the appropriate GoogleService-Info.plist into the app bundle based on the build configuration. Copy and paste the following shell script into the run script phase you just created:

# Name of the resource we're selectively copying
GOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist

# Get references to dev and prod versions of the GoogleService-Info.plist
# 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)
GOOGLESERVICE_INFO_DEV=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Dev/${GOOGLESERVICE_INFO_PLIST}
GOOGLESERVICE_INFO_PROD=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Prod/${GOOGLESERVICE_INFO_PLIST}

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

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

# Get a reference to the destination location for the GoogleService-Info.plist
PLIST_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
echo "Will copy ${GOOGLESERVICE_INFO_PLIST} to final destination: ${PLIST_DESTINATION}"

# Copy over the prod GoogleService-Info.plist for Release builds
if [ "${CONFIGURATION}" == "Release" ]
then
    echo "Using ${GOOGLESERVICE_INFO_PROD}"
    cp "${GOOGLESERVICE_INFO_PROD}" "${PLIST_DESTINATION}"
else
    echo "Using ${GOOGLESERVICE_INFO_DEV}"
    cp "${GOOGLESERVICE_INFO_DEV}" "${PLIST_DESTINATION}"
fi
Mashood Murtaza
  • 477
  • 5
  • 14
14

I found that in case of single target the only 100% viable way is to copy plist corresponding to build configuration during the build; but such answers here differ in details of how to do it, and no one was convenient enough for me. My answer is based on answer by @KnightFighter and this article on Medium.


Firstly add all different plists to project with different names (they must not be added to target as resources):

enter image description here

Next create user-defined build setting, where you can assign specific plist to each build configuration:

enter image description here

Finally add "Run script" phase with code:

GOOGLE_SERVICE_INFO_PLIST_SOURCE=${PROJECT_DIR}/${TARGET_NAME}/${GOOGLE_SERVICE_INFO_PLIST_FILENAME}

if [ ! -f $GOOGLE_SERVICE_INFO_PLIST_SOURCE ]
then
    echo "${GOOGLE_SERVICE_INFO_PLIST_SOURCE} not found."
    exit 1
fi

GOOGLE_SERVICE_INFO_PLIST_DESTINATION="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"

cp "${GOOGLE_SERVICE_INFO_PLIST_SOURCE}" "${GOOGLE_SERVICE_INFO_PLIST_DESTINATION}"

I think such way has some advantages:

  • no need to have folders hierarchy to store plists;
  • no need to duplicate file if single plist is used for several configurations;
  • it's easier to change filename in build settings than edit script if you need to add configuration or reassign plists; especially for non-programmers (i e build manager).
Varrry
  • 2,647
  • 1
  • 13
  • 27
  • I use multiple schemes & configurations (with only one target) so this solution works great for me. Thank for a very detail explanation. – nahung89 Aug 25 '21 at 05:48
7

Let's suppose we have two configurations set, develop and production. You have to make two things:

  1. Rename both plists to conform to given configuration:
  • GoogleService-Info-develop.plist
  • GoogleService-Info-production.plist
  1. Add a run script which copies the correct plist for selected configuration:
FIREBASE_PLIST_PATH="${PROJECT_DIR}/App/Resources/Plists/GoogleService-Info-${CONFIGURATION}.plist"
echo "Firebase plist path: ${FIREBASE_PLIST_PATH}"
cp -r ${FIREBASE_PLIST_PATH} "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"

A run script needs to be positioned before FirebaseCrashlytics script.

You you can init firebase as you did before for single scheme: FirebaseApp.configure()

Borut Tomazin
  • 8,041
  • 11
  • 78
  • 91
6

You cannot avoid to use the plist with Firebase. The best solution I found so far for you it would be to add both files and name it

GoogleService-Info_stage.plist

and

GoogleService-Info_prod.plist

Then from your code you can call the correct file. This way won't crash your app if you don't have the file. Just replace FILENAME with GoogleService-Info_prod or GoogleService-Info_stage.

if let configFile = Bundle.main.path(forResource: "FILENAME", ofType: "plist"), 
    let options = FirebaseOptions(contentsOfFile: configFile)   
{
   FirebaseApp.configure(options: options)
}
pierre23
  • 3,846
  • 1
  • 28
  • 28
6

So I have pondered the same question and using some ideas from earlier posts, some of which publish apps with GoogleServices-Info.plist for all environments in all apps and that is a bit of a concern.

I have come up with an extensible solution that copies the GoogleSerives-Info.plist file at build time. Further more this approach can support as many environments as you like with the ability to customise and follows a simple convention, making it easy to manage.

First and foremost i have three environments, debug (For running in simulator and device whist debugging and actively cutting code), staging (for deployment to test flight) and release for production.

Step one is to create your configuration(s):

enter image description here

Select "Product" -> "Scheme" -> "Edit Scheme" and duplicate/create new as required. Go through each Scheme and assign its respective configuration from the "Build Configuration" drop down in each of the categories:

enter image description here

I go a step further and uncheck "run" for Schemes that need to be distributed i.e. release and staging, and conversely uncheck "archive" for debug. You should do what makes sense for you.

enter image description here

Under build phases add the following run scrip (CONFIGURATIONS_FOLDER variable can be customised as desired - just ensure you use the same folder name in the next step):

# Get a reference to the folder which contains the configuration subfolders.
CONFIGURATIONS_FOLDER=Firebase
# Get a refernce to the filename of a 'GoogleService-Info.plist' file.
GOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist
# Get a reference to the 'GoogleService-Info.plist' for the current configuration.
GOOGLESERVICE_INFO_PLIST_LOCATION=${PROJECT_DIR}/${TARGET_NAME}/${CONFIGURATIONS_FOLDER}/${CONFIGURATION}/${GOOGLESERVICE_INFO_PLIST}
# Check if 'GoogleService-Info.plist' file for current configuration exist.
if [ ! -f $GOOGLESERVICE_INFO_PLIST_LOCATION ]
then
  echo "No '${GOOGLESERVICE_INFO_PLIST}' file found for the configuration '${CONFIGURATION}' in the configuration directory '${PROJECT_DIR}/${TARGET_NAME}/${CONFIGURATIONS_FOLDER}/${CONFIGURATION}'."
  exit 1
fi
# Get a reference to the destination location for the GoogleService-Info.plist.
GOOGLESERVICE_INFO_PLIST_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
# Copy 'GoogleService-Info.plist' for current configution to destination.
cp "${GOOGLESERVICE_INFO_PLIST_LOCATION}" "${GOOGLESERVICE_INFO_PLIST_DESTINATION}"
echo "Successfully coppied the '${GOOGLESERVICE_INFO_PLIST}' file for the '${CONFIGURATION}' configuration from '${GOOGLESERVICE_INFO_PLIST_LOCATION}' to '${GOOGLESERVICE_INFO_PLIST_DESTINATION}'."

enter image description here

In your chosen configurations folder ("Firebase" in the above example) nest folders for each configuration named exactly the same as its respective configuration (case sensitive), inside of which place the respective GoogleServices-Info.plist files like so:

enter image description here

Last but not least, i also like to ensure that a root level GoogleServices-Info.plist is not added into the project by accident so I add the following to my .gitignore.

# Ignore project level GoogleService-Info.plist
/[Project Name]/GoogleService-Info.plist
SagarScript
  • 1,145
  • 11
  • 15
6

Here's my version of @Essam's solution.

  1. Generate a GoogleServices version for the default scheme (Google-Services.plist) with the default identifier
  2. Generate a second GoogleServices version for the variant scheme (Google-Services-debug.plist) with the correct identifier
  3. Add both to the root of your project (where it tells you to in their guide)

Add this code where you'd add configure:

        let bundleID = Bundle.main.bundleIdentifier
        if (bundleID!.contains("debug")) {
            let resource: String = "GoogleService-Info-debug"
            let filePath = Bundle.main.path(forResource: resource, ofType: "plist")!
            let options = FirebaseOptions(contentsOfFile: filePath)
            FirebaseApp.configure(options: options!)
        } else {
            FirebaseApp.configure()
        }
Kyle Venn
  • 4,597
  • 27
  • 41
5

I solved this by this:

    #if STAGING
        if let filePath = Bundle.main.path(forResource: "GoogleService-Info-Dev", ofType: "plist"),
            let options = FirebaseOptions(contentsOfFile: filePath) {
                FirebaseApp.configure(options: options)
        } else {
            fatalError("GoogleService-Info-Dev.plist is missing!")
        }
    #else
        if let filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist"),
            let options = FirebaseOptions(contentsOfFile: filePath) {
                FirebaseApp.configure(options: options)
        } else {
            fatalError("GoogleService-Info.plist is missing!")
        }
    #endif
Arafin Russell
  • 1,487
  • 1
  • 18
  • 37
4

This is my solution!

NSString *filePath;
if([self isProduction]){
    filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
}else{
    filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info-Sandbox" ofType:@"plist"];
}
FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
[FIRApp configureWithOptions:options];

And That's it!

3

I think it is not possible to achieve without using the GoogleService-Info.plist. Because before you can begin integrating your iOS app with the Google Sign-In components, you must download the dependencies and configure your Xcode project. And this process shows that GoogleService-Info.plist has a big factor on it.

So the solutions and idea here in this SO question can help you with your problem. Just moved the main copy of the GoogleService-Info plist out of the app into 2 separate folders, then used the Build Phases "Copy Files" on each target to import the target specific plist into the Resources folder.

Also check this SO question, it might give you more information/idea to your problem.

Community
  • 1
  • 1
KENdi
  • 7,576
  • 2
  • 16
  • 31
  • Here, does target mean a different scheme for the same project? After reading the questions, I gathered that the target mean a different build target altogether and not just a different build scheme for the same target. – Varun Gupta Oct 20 '16 at 10:35
  • 1
    But what if you have one target with a different configuration for each scheme? – evya Jan 10 '18 at 13:20
3

If some of you fall into an error and Xcode complains

"Multiple commands produce GoogleService-Info.plist"

after applying @Knight Fighter response, you may want to:

  • Check Build Phases > Copy Bundle Resources
  • Filter for files named GoogleService-Info.plist
  • Remove any references you have to it, since it's already being copied through the script.
Luiz Dias
  • 1,947
  • 17
  • 22
2

Here's how to do it in Xamarin C#:

string plistPath = NSBundle.MainBundle.PathForResource ("GoogleService-Info", "plist");
Options options = new Options (plistPath);
App.Configure (options);

Remember to include the Firebase namespace:

using Firebase.Analytics;
Sune Kjærgård
  • 688
  • 6
  • 11
1

With Xcode 9.2, I have needed files for both targets to be named "googleServiceInfo.plist" but placed in different directories, with the directory/file for each target specified in "Build Phases", "Copy Bundle Resources".

The above was not my preferred solution, but I had previously tried using different filenames along the lines of @inidona's answer, converted to Swift 4:

 let filePath = Bundle.main.path(forResource: "googleServiceInfo-Pro", ofType: "plist")!
 let options = FirebaseOptions(contentsOfFile: filePath)
 FirebaseApp.configure(options: options!)

Unfortunately, this did not correct the Firebase error messages. In this question: Firebase iOS SDK - Using configuration file other than GoogleService-Info.plist generates console warning the original poster seems to have fixed by updating the Firebase Pod but I have not confirmed this.

djruss70
  • 1,023
  • 17
  • 23
1

@Vasily Bodnarchuk answer worked for me. The only thing that you need to pay attention is that the scripts in Xcode have a precise order, so you need to put this script as first one, before the scripts with

${PODS_ROOT}/FirebaseCrashlytics/run

and

"${PODS_ROOT}/FirebaseCrashlytics/upload-symbols" -gsp "${PROJECT_DIR}/<yourapp>/Configuration Files/GoogleService-Info-dev.plist" -p ios "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}"
1

For those who want to do it in Fastlane.

You can use the file manager plugin for fastlane (fastlane-plugin-file_manager), to run a simple copy command.

  1. Add a GoogleService-info-app.plist to your xcode project the standard way so it's linked properly.

  2. Use copy files to overwrite this linked file with the files you want in your build / beta lane.

    copy_files(source: "firebase/GoogleService-Info-" + ENV["APP_IDENTIFIER"] + ".plist", destination: "GoogleService-Info.plist")

Tosh
  • 1,789
  • 15
  • 20
1

though late to the party, I have a solution implemented for this. At first names your plists like below: GoogleService-Info-target1

GoogleService-Info-target2

GoogleService-Info-target3

And then add the below Script in the Build Phases Tabs of each target by adding a new runs script phase:

PATH_TO_PLISTS="${PROJECT_DIR}/${PROJECT_NAME}/(Folder Name containing all the plists)" case "${TARGET_NAME}" in

"target1 name" ) cp -r "$PATH_TO_PLISTS/GoogleService-Info-target1.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

"target2 name" ) cp -r "$PATH_TO_PLISTS/GoogleService-Info-target2.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

"target3 name" ) cp -r "$PATH_TO_PLISTS/GoogleService-Info-target3.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

*) ;; esac

Raja Saad
  • 350
  • 4
  • 12
1

!Finally it working for macOS App.

Thanks @vasily-bodnarchuk for his solution for iOS app.But for macOS app it needs little extra modification in script file.

Just append the designated resources directory for macOS "Contents\Resources". Please check for detail Copy Bundle Resources

Code

PATH_TO_GOOGLE_PLISTS="${PROJECT_DIR}/SM2/Application/Firebase"

case "${CONFIGURATION}" in

   "Debug_Staging" | "AdHoc_Staging" )
        cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-dev.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Contents/Resources/GoogleService-Info.plist" ;;

   "Debug_Poduction" | "AdHoc_Poduction" | "Distribution" | "Test_Poduction" )
        cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-prod.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Contents/Resources/GoogleService-Info.plist" ;;

    *)
        ;;
esac
1

Place Your GoogleService-Info-dev.plist,GoogleService-Info-prod.plist file in some folder in your root directory

eg: Place your files in some directory from your root

Add Build Phase Script

# Set the path to the googleServiceInfo folder
GOOGLE_SERVICE_INFO_PATH="${PROJECT_DIR}/googleServiceInfo"

GENERATED_INFO_FILE_PATH="${PROJECT_DIR}/${PRODUCT_NAME}"

# Determine the appropriate GoogleService-Info.plist file based on the environment
if [[ "${CONFIGURATION}" == "Debug-dev" || "${CONFIGURATION}" == "Release-dev" ]]; then
    cp -r "$GOOGLE_SERVICE_INFO_PATH/GoogleService-Info-dev.plist" "$GENERATED_INFO_FILE_PATH/GoogleService-Info.plist"
elif [[ "${CONFIGURATION}" == "Debug-production" || "${CONFIGURATION}" == "Release-production" || "${CONFIGURATION}" == "Profile-production" ]]; then
    cp -r "$GOOGLE_SERVICE_INFO_PATH/GoogleService-Info-prod.plist" "$GENERATED_INFO_FILE_PATH/GoogleService-Info.plist"
fi

Inside this script you can see build-variants, you can get from info Section eg: enter image description here

Don't Forget to add output files inside your build phase script

Place Output files in your build phase script

Move your build phase script(here its renamed as GoogleServiceInfo) above Compile Sources

Keynote: Check logs while running the build script. Here, target name was Runner, you can change it according.

enter image description here

0

For multiple schemes like Stage, QA, UAT and PROD with one target below script worked for me. Also i have maintained .xcconfig files with their configuration name

 # Get a reference to the folder which contains the configuration 
   subfolders.
    CONFIGURATIONS_FOLDER=Firebase
 # Get a refernce to the filename of a 'GoogleService-Info.plist' file.
    GOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist
 # Get a reference to the 'GoogleService-Info.plist' for the current 
   configuration. ENV_NAME name is the folder name(Dev,QA,Uat,Prod) 
   which i have in my .xcconfig file for each environment.
    GOOGLESERVICE_INFO_PLIST_LOCATION=${PROJECT_DIR}/${TARGET_NAME}
    /${CONFIGURATIONS_FOLDER}/${ENV_NAME}/${GOOGLESERVICE_INFO_PLIST}
 # Check if 'GoogleService-Info.plist' file for current configuration 
   exist.
    if [ ! -f $GOOGLESERVICE_INFO_PLIST_LOCATION ]
   then
    echo "No '${GOOGLESERVICE_INFO_PLIST}' file found for the 
    configuration '${CONFIGURATION}' in the configuration directory 

    '${PROJECT_DIR}/${TARGET_NAME}/${CONFIGURATIONS_FOLDER}/
    ${CONFIGURATION}'."
   exit 1
 fi
  # Get a reference to the destination location for the GoogleService- 
    Info.plist.
     GOOGLESERVICE_INFO_PLIST_DESTINATION=${BUILT_PRODUCTS_DIR}
     /${PRODUCT_NAME}.app
  # Copy 'GoogleService-Info.plist' for current configution to 
    destination.
     cp "${GOOGLESERVICE_INFO_PLIST_LOCATION}" 
     "${GOOGLESERVICE_INFO_PLIST_DESTINATION}"
     echo "Successfully coppied the '${GOOGLESERVICE_INFO_PLIST}' file 
     for the '${CONFIGURATION}' configuration from 
     '${GOOGLESERVICE_INFO_PLIST_LOCATION}' to 
     '${GOOGLESERVICE_INFO_PLIST_DESTINATION}'."
Madhu
  • 869
  • 1
  • 17
  • 37