How to use different Google configurations for each build config
If you have ever used Google Sign-In, Firebase, or Google Analytics for iOS, you should be familiar with a GoogleService-Info.plist
configuration file. Using property list files instead of setting every required property manually in code is convenient, especially while using more than one Google service at once and the configuration grows. But have you ever wondered how this can be adjusted to multiple environments?
Environments
To set up multiple environments in iOS application I use build configurations. By default, every iOS app has two build configurations: Debug and Release. In our projects we usually have three:
- Develop - application for developers with debug enabled, usually connecting to the Stage backend.
- Stage - signed application connecting to the Stage backend for early TestFlight testing.
- Prod - signed application connecting to the production backend, published to the store after UAT (User Acceptance Testing) on TestFlight.
You can define build configurations in the project settings ([PROJECT]→Info).
User-Defined settings
Build configurations let you easily use different User-Defined settings for each configuration. You can define these at the bottom of target build settings ([TARGET]→Build Settings).
Respectively, you can use User-Defined settings in the Info.plist file.
In the example above, the application will use different FacebookAppID
properties for each build configuration. FacebookAppID
property is automatically used by the Facebook SDK for iOS, but you can also refer to these values directly in the code:
if let path = Bundle.main.path(forResource: "Info", ofType: "plist"),
let dict = NSDictionary(contentsOfFile: path) as? [String: AnyObject] {
// read values from dict
}
Google configuration
One might think that you can do the same with GoogleService-Info.plist
configuration file. Unfortunately, you cannot read User-Defined settings from custom property list files, you can only do that in the Info.plist files defined in the target Build Settings.
Considering there can be only one GoogleService-Info.plist
file and it is not possible to read User-Defined settings from custom .plist files, how would you set up different Google configuration for each environment?
Run Scripts to the rescue
Fortunately, you can add new Run Script Phase to target Build Phases that will copy a proper Google configuration file to default GoogleService-Info.plist
place.
The script is really simple and it only copies a plist file from Resources using a predefined CONFIGURATION
variable that points to the build configuration. In the build configurations listed above, it replaces GoogleService-Info.plist
file content with one of the following files stored in Resources:
- GoogleService-Info-Develop.plist
- GoogleService-Info-Stage.plist
- GoogleService-Info-Prod.plist
cp "${SRCROOT}/src/Resources/GoogleServiceInfoPlists/GoogleService-Info-$CONFIGURATION.plist" "${SRCROOT}/src/GoogleService-Info.plist"
SRCROOT
is also predefined and it points to the project location.
Note that src/GoogleService-Info.plist
must be added to target Copy Bundle Resources build phase, while Google configuration files to be copied from resources not necessarily.
Caution!
The Run Script that updates GoogleServiceInfo.plist
file must be dragged before Copy Bundle Resources phase. Otherwise, it will not work because the default Google configuration file will be used.
Alternatives
Alternatively, you can set Google Client ID and other Google configuration properties directly in the code, for example:
GIDSignIn.sharedInstance().clientID = "CLIENT_ID"
To keep different Google configuration for each environment you can read a proper Google plist file according to build configuration or store Google configuration on your own, without property list files.
To determine build configuration at runtime you can use Other Swift Flags in target Build Settings. For each build configuration add a flag with environment starting with -D
, like -DDEVELOP
.
Then you can easily determine build configuration at runtime:
#if DEVELOP
// Develop
#elseif STAGE
// Stage
#elseif PROD
// Prod
#endif
This post was inspired by this StackOverflow topic.