Use different GoogleService-Info.plist for different build schemes
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If your iOS app uses Firebase in more than one environment, you should not swap GoogleService-Info.plist by hand before every build. The robust setup is to keep one plist per environment and let Xcode copy the correct file for the active build configuration or scheme. That keeps development, staging, and production pointed at the right Firebase project without manual edits.
Organize the Firebase Config Files
Start by storing each plist under a distinct path in the repository. For example:
Do not add all three plists to the app target as regular bundled resources. If you do, Xcode may copy multiple files with the same name into the bundle and the build becomes fragile.
The safer pattern is to keep them in the project navigator for reference, but copy exactly one of them during the build.
Tie Schemes to Build Configurations
A scheme chooses a build configuration. That means the easiest setup is:
- '
Debugscheme usesDebugconfiguration' - '
Stagingscheme usesStagingconfiguration' - '
Releasescheme usesReleaseconfiguration'
Then create a user-defined build setting on the app target, for example FIREBASE_PLIST_PATH, and assign a different value per configuration:
This is better than hardcoding paths inside a shell script because the build settings stay visible in Xcode and can also live in .xcconfig files if your project uses them.
Copy the Right File During the Build
Add a Run Script Phase before Copy Bundle Resources or near the end of build phases. The script should copy the selected plist into the built app bundle under the fixed name Firebase expects.
The app still sees the usual GoogleService-Info.plist file name at runtime, but the source file now depends on the active configuration.
Optional .xcconfig Setup
If your project already uses configuration files, putting the plist path there keeps the Xcode UI cleaner.
The copy script stays the same. Only the build setting changes.
Verify the Active Configuration at Runtime
If you want a quick sanity check during development, print the selected Firebase app options after startup:
That makes it obvious when a staging build accidentally points at production.
Why Schemes Alone Are Not Enough
A common misunderstanding is that schemes themselves directly choose files. They do not. Schemes choose targets, actions, and build configurations. The file selection logic still needs to live in build settings, scripts, or target membership.
That is why the stable solution is "scheme to configuration, configuration to plist path, script copies one file." Once you think in those layers, the setup is easy to maintain.
Common Pitfalls
The biggest mistake is adding every GoogleService-Info.plist to Copy Bundle Resources. That often produces duplicate-file warnings or unpredictable results depending on build order.
Another mistake is using one plist file name per environment, such as GoogleService-Info-Debug.plist, and forgetting to rename it to GoogleService-Info.plist inside the app bundle. Firebase expects the standard file name unless you configure it manually in code.
Teams also often duplicate schemes but forget to create matching build configurations. In that case, multiple schemes still use the same plist path and the environment separation is only cosmetic.
Finally, do not rely on developers remembering to drag the correct plist into the project before archiving. That workflow will eventually ship the wrong Firebase credentials.
Summary
- Keep one
GoogleService-Info.plistper environment in separate paths. - Map each build configuration to a
FIREBASE_PLIST_PATHvalue. - Use a run script phase to copy exactly one plist into the bundle as
GoogleService-Info.plist. - Schemes choose configurations; configurations choose the plist path.
- Avoid manual file swapping and avoid copying multiple plist files into the app bundle.

