Use different GoogleService-Info.plist for different build schemes
IosGoogle Cloud-MessagingGoogle SigninIos Problem Overview
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!
Ios Solutions
Solution 1 - Ios
Details
Tested on:
- Xcode 9.2
- Xcode 10.2 (10E125)
- Xcode 11.0 (11A420a)
Solution
- Create folder with all your Google.plist files (with different names) in project
- Add run script
> 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_Poduction" | "AdHoc_Poduction" | "Distribution" | "Test_Poduction" )
cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-prod.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;
*)
;;
esac
> Build schemes names
Solution 2 - Ios
@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!)
Solution 3 - Ios
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
:
That's it :)
Solution 4 - Ios
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.
-
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. -
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 differentGoogleService-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.
Solution 5 - Ios
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];
Solution 6 - Ios
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"
- Select the folder containing the target's
GoogleService-Info.plist
, make sureCopy items if needed
andCreate groups
are selected, check only the corresponding target in the list of targets, and pressAdd
That's it. Now you should have something similar to this structure
When you build a target, the correct GoogleService-Info.plist
will be used.
Solution 7 - Ios
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:
Solution 8 - Ios
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 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
Solution 9 - Ios
Let's suppose we have two configurations set, develop
and production
. You have to make two things:
- Rename both plists to conform to given configuration:
- GoogleService-Info-develop.plist
- GoogleService-Info-production.plist
- 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()
Solution 10 - Ios
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):
Next create user-defined build setting, where you can assign specific plist to each build configuration:
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).
Solution 11 - Ios
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)
}
Solution 12 - Ios
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!
Solution 13 - Ios
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
Solution 14 - Ios
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.
Solution 15 - Ios
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):
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:
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.
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}'."
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:
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
Solution 16 - Ios
Here's my version of @Essam's solution.
- Generate a GoogleServices version for the default scheme
(
Google-Services.plist
) with the default identifier - Generate a second GoogleServices version
for the variant scheme (
Google-Services-debug.plist
) with the correct identifier - 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()
}
Solution 17 - Ios
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.
Solution 18 - Ios
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;
Solution 19 - Ios
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: https://stackoverflow.com/questions/40530027/firebase-ios-sdk-using-configuration-file-other-than-googleservice-info-plist?rq=1 the original poster seems to have fixed by updating the Firebase Pod but I have not confirmed this.
Solution 20 - Ios
@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}"
Solution 21 - Ios
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.
-
Add a GoogleService-info-app.plist to your xcode project the standard way so it's linked properly.
-
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")