firebase / firebase/firebase-ios-sdk
Firebase Remote Config - Support multiple Firebase apps with individual namespaces
- Dominant language
- C++
- Stars
- 6.7k
- Forks
- 1.8k
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 75
Description
### Description
We have an iOS app that allows users to pick target environment at runtime. Each environment has the each own `plist` file, ie GoogleService-Info-Staging.plist or GoogleService-Info-Prod.plist.
Whenever user switch environment, I teardown the FIrebase
```
private(set) var remoteConfig: RemoteConfig?
public func tearDownFirebase() async {
if let app = FirebaseApp.app() {
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(false)
remoteConfig = nil
await app.delete()
}
}
```
Then reinitiate the Firebase and RemoteConfig
```
public init(plist: Plist) {
if FirebaseApp.app() == nil {
guard let plistPath = Bundle.module.path(forResource: plist, ofType: "plist"),
let firebaseOptions = FirebaseOptions(contentsOfFile: plistPath)
else {
BP.Log.log(Events.firebaseStartedWithoutOptions(env: plist))
FirebaseApp.configure()
return
}
FirebaseApp.configure(options: firebaseOptions)
}
remoteConfig = RemoteConfig.remoteConfig()
}
public func startRemoteConfig(environment: EnvironmentName) async throws
-> RemoteConfigFetchStatus
{
guard let remoteConfig else { throw BPFirebaseError.remoteConfigInstance }
remoteConfig.setDefaults(RemoteConfigKey.defaults as? [String: NSObject])
remoteConfig.configSettings = RemoteConfigSettings()
let expirationDuration =
self.isDeveloperMode(environment: environment)
? 0
: RemoteConfigValues.shared.cacheExpiration
do {
let result = try await remoteConfig.fetch(withExpirationDuration: expirationDuration)
try await remoteConfig.activate()
return result
} catch {
throw BPFirebaseError.remoteConfigFetch
}
}
```
I am able to see that the `.plist` file for the target environment is being properly passed in and invoked. The FirebaseApp.app() also has different address after the environment is switched.
The problem is with the remote config. It always shows the first values being pulled. So for example, if I start with "Staging" env, and then trying to switch to "Production, after the re-initialization process is complete, it keeps showing Staging remote config values.
Questions:
• Did I do the correct steps and procedures to `teardown` the Firebase?
• Is there specific steps I need to perform to "reset" the previous `RemoteConfig.remoteConfig()` to make sure it pulls in the correct value when users switch environment?
• Does Remote Config support switching environment at runtime?
### Reproducing the issue
• Make sure you have different `.plist` file for each target environment
• Allow users to switch environments
• When the switch is requested, make sure the proper `plist` is used to create the FirebaseApp.app()
• Check that remote config pulls in the right values for a specific environment
### Firebase SDK Version
10.24.0
### Xcode Version
15.3
### Installation Method
Swift Package Manager
### Firebase Product(s)
Crashlytics, Firestore, Remote Config
### Targeted Platforms
iOS
### Relevant Log Output
```shell
-- INITIAL ENVIRONMENT --
BP.app/BPFirebase_BPFirebase.bundle/GoogleService-Info-NPD.plist
2024-04-30 21:42:45.944081-0600 BP[74735:36948440] [Firebase/Crashlytics] Version 10.24.0
RemoteConfig activated: Optional() | app:
RemoteConfig fetched
-- SWITCH TO STAGING ENVIRONMENT --
BP.app/BPFirebase_BPFirebase.bundle/GoogleService-Info-Staging.plist
RemoteConfig activated: Optional() | app:
RemoteConfig fetched
```
### If using Swift Package Manager, the project's Package.resolved
Expand Package.resolved snippet
```json
{
"pins" : [
{
"identity" : "abseil-cpp-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/abseil-cpp-binary.git",
"state" : {
"revision" : "748c7837511d0e6a507737353af268484e1745e2",
"version" : "1.2024011601.1"
}
},
{
"identity" : "app-check",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
"revision" : "c218c2054299b15ae577e818bbba16084d3eabe6",
"version" : "10.18.2"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
"state" : {
"revision" : "42eae77a0af79e9c3f41df04a23c76f05cfdda77",
"version" : "10.24.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "51ba746a9d51a4bd0774b68499b0c73ef6e8570d",
"version" : "10.24.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "a637d318ae7ae246b02d7305121275bc75ed5565",
"version" : "9.4.0"
}
},
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "26c898aed8bed13b8a63057ee26500abbbcb8d55",
"version" : "7.13.1"
}
},
{
"identity" : "grpc-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/grpc-binary.git",
"state" : {
"revision" : "e9fad491d0673bdda7063a0341fb6b47a30c5359",
"version" : "1.62.2"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "9534039303015a84837090d20fa21cae6e5eadb6",
"version" : "3.3.2"
}
},
{
"identity" : "interop-ios-for-google-sdks",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
"state" : {
"revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
"version" : "100.0.0"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
"version" : "2.4.0"
}
},
{
"identity" : "servicecontext",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ribtiago/ServiceContext.git",
"state" : {
"revision" : "f3ebe4fd8374ecdb8e31201d493772dc6eaab7c3",
"version" : "2.0.1"
}
},
{
"identity" : "snapkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SnapKit/SnapKit",
"state" : {
"revision" : "d458564516e5676af9c70b4f4b2a9178294f1bc6",
"version" : "5.0.1"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb",
"version" : "1.1.0"
}
},
{
"identity" : "swift-package-manager-google-mobile-ads",
"kind" : "remoteSourceControl",
"location" : "https://github.com/googleads/swift-package-manager-google-mobile-ads.git",
"state" : {
"revision" : "5ff977255c2ba5844e7e9da779f1f2a5a00e0028",
"version" : "11.2.0"
}
},
{
"identity" : "swift-package-manager-google-user-messaging-platform",
"kind" : "remoteSourceControl",
"location" : "https://github.com/googleads/swift-package-manager-google-user-messaging-platform.git",
"state" : {
"revision" : "cfe8b2ae16b9bc81f4cdf1d1a12a01a452489c32",
"version" : "2.3.0"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "9f0c76544701845ad98716f3f6a774a892152bcb",
"version" : "1.26.0"
}
},
{
"identity" : "swiftuibackports",
"kind" : "remoteSourceControl",
"location" : "https://github.com/shaps80/SwiftUIBackports.git",
"state" : {
"revision" : "dfc492b9907d1c9b78f3ef928add759b798c464d",
"version" : "1.0.9"
}
},
{
"identity" : "swiftuiextras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ribtiago/SwiftUIExtras.git",
"state" : {
"revision" : "b40c865b2756a0c07145e065f078d5a4c591d08f",
"version" : "1.9.3"
}
}
],
"version" : 2
}
```
### If using CocoaPods, the project's Podfile.lock
Expand Podfile.lock snippet
```yml
Replace this line with the contents of your Podfile.lock!
```
Contributor guide
Research direction
Start by tracing the RemoteConfig.remoteConfig() and FirebaseApp.app() entry points, including the app.delete() lifecycle described in the report. Reproduce the environment switch with separate plist files and determine the SDK behavior for multiple app instances; done means each app reliably fetches and exposes its own namespace without retaining the prior values.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ios, swift
- Domain
- mobile
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100