Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firebase Remote Config - Support multiple Firebase apps with individual namespaces #12876

Open
asetiyadi-MLB opened this issue May 1, 2024 · 10 comments

Comments

@asetiyadi-MLB
Copy link

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

-- 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(<FIRRemoteConfig: 0x600002aff960>) | app: <FIRApp: 0x600000082c70>
RemoteConfig fetched

-- SWITCH TO STAGING ENVIRONMENT --
BP.app/BPFirebase_BPFirebase.bundle/GoogleService-Info-Staging.plist
RemoteConfig activated: Optional(<FIRRemoteConfig: 0x600002aff960>) | app: <FIRApp: 0x60000023a580>
RemoteConfig fetched

If using Swift Package Manager, the project's Package.resolved

Expand Package.resolved snippet
{
  "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
Replace this line with the contents of your Podfile.lock!
@google-oss-bot
Copy link

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

@paulb777
Copy link
Member

paulb777 commented May 1, 2024

It looks like you need to use the remoteConfig(app:) API

@asetiyadi-MLB
Copy link
Author

@paulb777 .. I tried using this

guard let app = FirebaseApp.app() else { return }
remoteConfig = RemoteConfig.remoteConfig(app: app)

and I am still not getting the right remote config values for the intended environment (after the runtime switch)

@paulb777
Copy link
Member

paulb777 commented May 1, 2024

@asetiyadi-MLB
Copy link
Author

@paulb777 I did some testing using the non-default implementation. Following the sample codes however I am still not getting the remoteConfig values after switching environment.

public init(plist: Plist) {
    guard let plistPath = Bundle.module.path(forResource: plist, ofType: "plist"),
          let firebaseOptions = FirebaseOptions(contentsOfFile: plistPath)
    else {
      fatalError("💥 Invalid GoogleService plist info")
    }

    FirebaseApp.configure(name: appName, options: firebaseOptions)

    guard let app = FirebaseApp.app(name: appName)
    else {
      fatalError()
    }
    
    Self.firebaseApp = app
    self.firestoreConfiguration = FirestoreConfiguration(app: app)
    Self.config = RemoteConfig.remoteConfig(app: app)
  }

The iOS app is pointing to the correct Firestore environment, only the remoteConfig is having the issue.

The other thing that I read from the remoteConfig(app:) API, this will cause FirebaseAnalytics to not working with non-default app. We use FirebaseAnalytics on our app. Is there no workaround for this?

@paulb777
Copy link
Member

paulb777 commented May 2, 2024

Right no workaround for that. FirebaseAnalytics only works with the default app #230

@asetiyadi-MLB
Copy link
Author

That is a bummer on the FirebaseAnalytics.

As for the remoteConfig itself, has it ever been proven to be working if we use remoteConfig(app:) it can support changing environment at runtime?

@paulb777
Copy link
Member

paulb777 commented May 2, 2024

Sorry about the trouble here. Looking at the code in more detail, it seems that a single namespace - FIRNamespaceGoogleMobilePlatform is used for all apps.

I'll follow up with the team to clarify if this is a bug or a missing feature.

@paulb777
Copy link
Member

paulb777 commented May 3, 2024

Yes, it turns out that Remote Config manages all Firebase apps in a single namespace, so that the functionality does not work as it would be expected. Changing the behavior would be a large task and not something we can prioritize now. Also, it might break apps relying on the current behavior. I'll rename the issue and we can use it to track thumbs-up requests for proper multi-Firebase app support.

@paulb777 paulb777 changed the title Firebase Remote Config - Switch Environment at runtime not pulling the right values Firebase Remote Config - Support multiple Firebase apps with individual namespaces May 3, 2024
@asetiyadi-MLB
Copy link
Author

@paulb777 Thank you for the clarification. It would be great if Firebase can support multi environments (especially for switching environment at runtime) for both Remote Config and Analytics. Sounds like this is a feature that have been requested before as well. It would be a very welcome addition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants