firebase / firebase/firebase-ios-sdk

Firebase Remote Config - Support multiple Firebase apps with individual namespaces

Open
#12,876 10 comments 0 reactions 0 assignees View on GitHub
api: remoteconfig type: feature request
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.