'In new Firebase, how to use multiple config file in xcode?
Hey I am playing with the new firebase iOS SDK, in my project, I have only one target, I created two configurations, Debug and Release, with different bundler identifier, but seems like the config file downloaded from firebase only support one bundle identifier.
So anyone know how to use firebase within a multiple bundle identifier xcode project?
Thanks!
Solution 1:[1]
Update: see Firebase official documentation https://firebase.google.com/docs/projects/multiprojects
Updated with an easier solution:
- keep the same names for both GoogleService-Info.plist
- put one GoogleService-Info.plist inside a subfolder, say "staging"
- add references to both files in Xcode while linking them to corresponding targets
- just use FIRApp.configure() in your AppDelegate, done
My first attempt that didn't solve the issue:
I renamed the second GoogleService-Info.json to something else and used following code to configure Firebase from AppDelegate.
// Swift code
#if STAGING
let firebasePlistFileName = "GoogleService-Staging-Info"
#else
let firebasePlistFileName = "GoogleService-Info"
#endif
let firbaseOptions = FIROptions(contentsOfFile: NSBundle.mainBundle().pathForResource(firebasePlistFileName, ofType: "plist"))
FIRApp.configureWithOptions(firbaseOptions)
If I run the Staging target, I will receive Firebase's complaint about 'Could not locate configuration file: 'GoogleService-Info.plist'.'. But then it says '<FIRAnalytics/INFO> Firebase Analytics v.3300000 started'.
As I checked on the Firebase dashboard, both production and staging apps have incoming user events being logged.
The above-mentioned configureWithOptions method is not documented in Firebase documentation, I figured it out by checking its source code. I'm not sure about what could be the downside of calling this. I would love to hear other approaches.
Solution 2:[2]
I've implemented something similar, as I've had two schemes for a single target that have different bundle identifiers. In my examples below, I have two different schemes, one for UAT and one for PROD.
Create the two GoogleService-Info.json files, and put them into your project directory (not Xcode project) in different folders, e.g.
ROOT/config/UAT/GoogleService-Info.json
ROOT/config/PROD/GoogleService-Info.json
Then add the files to your Xcode project like so:
Now you need to add a Run Script in your Build Phases. This will need to be added before the Compile Sources stage:
This Run Script takes the appropriately located json file and duplicates it into the build app directory, meaning Firebase/Google will identify it identically to how it would identify the file in a single identifier setup.
isUAT=`expr "$GCC_PREPROCESSOR_DEFINITIONS" : ".*UAT=\([0-9]*\)"`
RESOURCE_PATH=${SRCROOT}/${PRODUCT_NAME}/config/PROD
if [ $isUAT = 1 ]; then
RESOURCE_PATH=${SRCROOT}/${PRODUCT_NAME}/config/UAT
fi
BUILD_APP_DIR=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
echo "Copying all files under ${RESOURCE_PATH} to ${BUILD_APP_DIR}"
cp -v "${RESOURCE_PATH}/"* "${BUILD_APP_DIR}/"
I would estimate you can use this same logic for Google Analytics as well, which uses a similar json config file setup.
Solution 3:[3]
Better approach if you have single target is to just name your configs appropriately and load them as below. Seems to work fine for UAT environment example : Swift 3.0
var fireBaseConfigFile = Bundle.main.path(forResource: "GoogleService-Info-PROD", ofType: "plist")
#if UAT
fireBaseConfigFile = Bundle.main.path(forResource: "GoogleService-Info-UAT", ofType: "plist")
#endif
guard let firOptions = FIROptions(contentsOfFile: fireBaseConfigFile) else {
assert(false, "Failed to load Firebase config file")
return
}
FIRApp.configure(with: firOptions)
Solution 4:[4]
Add both Google Service plists to your project, with different names:
Then for each Build Scheme, select Edit Scheme and add a unique value for an environment variable like so:
So the other build scheme's BUILD_FOR environment variable would be set to "DEV", for example.
Then, you would check for this variable in App Delegate and configure the FIRApp according to which scheme was built:
let buildFor = ProcessInfo.processInfo.environment["BUILD_FOR"]! as String
var firebasePlistFileName = "GoogleService-Info"
if buildFor == "PROD" {
firebasePlistFileName = "GoogleService-Prod-Info"
}
let firbaseOptions = FIROptions(contentsOfFile: Bundle.main.path(forResource: firebasePlistFileName, ofType: "plist"))
FIRApp.configure(with: firbaseOptions!)
Solution 5:[5]
I did store 2 GoogleService-Info with different names:
GoogleService-Info.plist
for productionGoogleService-Info-Debug.plist
for development
And then go to Build Phases
, add new run script:
if [ "${CONFIGURATION}" == "Release" ]; then
cp -r "${PROJECT_DIR}/PathToYourGoogleServiceInfoFile/GoogleService-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
echo "Production plist copied"
elif [ "${CONFIGURATION}" == "Debug" ]; then
cp -r "${PROJECT_DIR}/PathToYourGoogleServiceInfoFile/GoogleService-Info-Debug.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
echo "Development plist copied"
fi
Solution 6:[6]
Using Objective-C, in your AppDeletage.m:
NSString *filePath;
#ifdef DEBUG
filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info-Debug" ofType:@"plist"];
#else
filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info-Production" ofType:@"plist"];
#endif
FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
[FIRApp configureWithOptions:options];
Solution 7:[7]
I see a lot of responses using Targets, but if you want to differentiate by scheme you can go to:
Edit Schemes... > Select Scheme > Build > Post-Action
- Add a Run Script
- Provide build settings from: (select the target you want to)
- Add following script
PATH_TO_GOOGLE_PLISTS="${PROJECT_DIR}/MVCVMRC/Supporting Files/FirebasePlist"
cp -r "$PATH_TO_GOOGLE_PLISTS/Brazil/GoogleService-Info-preprod.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
Don't forget to change the PATH_TO_GOOGLE_PLISTS
Solution 8:[8]
Firebase includes official documentation for how to do this now. I used this method in my app and it works great.
More specifically, I defined a custom Swift flag "BUILD_TYPE" in XCode and for each scheme, I define a unique value for that flag. Then, in my AppDelegate, I tell Firebase what GoogleService-___.plist
file to load at runtime by this Swift flag value:
let filePath = Bundle.main.path(forResource: "GoogleService-" + Bundle.main.infoDictionary!["BUILD_FLAVOR"] as! String, ofType: "plist")
guard let fileopts = FirebaseOptions.init(contentsOfFile: filePath!) else {
fatalError("Couldn't load config file")
}
FirebaseApp.configure(options: fileopts)
Solution 9:[9]
The latest syntax is here to configure firebase for 2 targets (2 Plist files)
func configureFirebase() {
let firebasePlistFileName = ConfigurationManager.sharedManager().firebasePlistName()
if let path = Bundle.main.path(forResource: firebasePlistFileName, ofType: "plist") {
if let firbaseOptions = FirebaseOptions(contentsOfFile: path) {
FirebaseApp.configure(options: firbaseOptions)
}
}
}
firebasePlistFileName variable is just fetching the PList name based on current configuration i.e. Development or Production.
Solution 10:[10]
For Swift 5, if you have different environnements, just do:
guard let options = FirebaseOptions(contentsOfFile: Bundle.main.path(forResource: MYConstants.myFileName, ofType: "plist") ?? "") else {
fatalError(false, "Failed to load Firebase config file")
}
FirebaseApp.configure(options: options)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | benomatis |
Solution 2 | Tiago Almeida |
Solution 3 | Nikhil |
Solution 4 | |
Solution 5 | Tai Le |
Solution 6 | |
Solution 7 | RodolfoAntonici |
Solution 8 | Sam |
Solution 9 | Hemant Sharma |
Solution 10 |