firebase / firebase/firebase-unity-sdk
[FR]: Don't let a Play-Services-required product's init failure break other Firebase products sharing the default FirebaseApp
- Dominant language
- C#
- Stars
- 320
- Forks
- 60
- Avg merge
- 11h 15m
- Merged PRs (30d)
- 11
Description
### Description
1. Use case: An app integrates multiple Firebase products together — e.g. Analytics, Crashlytics, and Remote Config (all documented "Recommended" or "Not Required" per [Dependencies on Google Play services](https://firebase.google.com/docs/android/android-play-services)) alongside Cloud Messaging (documented "Required"). The app needs to keep working, with only the Required product unavailable, on devices without Google Play Services or with broken/outdated GMS — e.g. Huawei AppGallery devices, or (as in [firebase/firebase-unity-sdk#1170](https://github.com/firebase/firebase-unity-sdk/issues/1170)) Meta Quest 3.
2. How the SDK currently handles it: Accessing FirebaseApp.DefaultInstance — which every product needs — triggers FirebaseApp.CreateAndTrack(), which eagerly discovers and initializes every Firebase component linked into the build, including ones the app never explicitly calls into. If a Required product fails this eager init because Play Services is unavailable, the resulting Firebase.InitializationException: Firebase modules failed to initialize: (missing dependency) is then thrown by every subsequent native call from any other product that touches CreateAndTrack() — not just the first.
We reproduced this concretely on Android (real Huawei device + a clean emulator with no Google APIs at all): with Cloud Messaging as the Required product, Crashlytics.SetCustomKey(), FirebaseRemoteConfig.get_DefaultInstance(), FirebaseRemoteConfig.get_Keys(), and FirebaseRemoteConfig.SetDefaultsAsync() all throw the identical exception — even though Analytics/Crashlytics/Remote Config are each independently documented as working without Play Services. This isn't triggered by application code: it happens automatically via FirebaseInitProvider's auto-init at process start, confirmed by log timestamps to occur ~3 seconds before our app's first Firebase-related call, and ~1.3 seconds before our app's very first log line at all.
We confirmed with Firebase support (case closed, "expected behavior") that:
firebase_messaging_auto_init_enabled=false does not prevent this — it only gates automatic token generation, not component registration/dependency checking.
Only Remote Config exposes a secondary (non-default) FirebaseApp instance API in the Unity wrapper; Analytics and Crashlytics do not, so there's no workaround via secondary instances either (and even for Remote Config, we found a secondary instance didn't actually avoid the failure in practice).
The only mitigation offered was maintaining a separate build variant with the Required product removed entirely for GMS-less markets — an all-or-nothing tradeoff for what's really a per-device runtime condition.
This exact class of issue was previously reported in [firebase/firebase-unity-sdk#170](https://github.com/firebase/firebase-unity-sdk/issues/170) (2022, with Dynamic Links as the Required product instead of Cloud Messaging), and may be related to the question raised in [firebase/firebase-unity-sdk#1170](https://github.com/firebase/firebase-unity-sdk/issues/1170) (2025). We're filing this specifically against the Unity SDK — we haven't verified whether the native Android/iOS SDKs exhibit the same cross-product behavior, since our reproduction and the CreateAndTrack() call stack we observed are from the Unity wrapper.
3. How we propose the SDK should handle it:
Each Firebase product should be independently queryable and independently safe to use, instead of sharing one all-or-nothing initialization outcome:
A failure in one component's own dependency resolution (e.g. Cloud Messaging failing on GMS) should not cause FirebaseApp.CreateAndTrack() (or its equivalent) to throw when a completely unrelated component (e.g. Crashlytics, Remote Config) is touched. The native layer's eager validation should be scoped per-component, not global — a broken branch shouldn't take down the rest.
On top of that, expose a per-product "is it safe to use this component" check, so an app can decide per-service whether to call into it — the same pattern we already use ourselves for the whole-device GMS check (CheckAndFixDependenciesAsync()), just at the right granularity. Today that check only tells you the aggregate device-wide GMS status, not which specific product is actually broken. Critically, this check shouldn't be a required safety net: even if an app skips it, or calls into a product that turns out to be unavailable, that call should fail gracefully and locally, without affecting unrelated components.
### API Proposal
bool FirebaseApp.IsComponentAvailable(Type componentType);
### Firebase Product(s)
Analytics, Crashlytics, Remote Config, Messaging, All
### Targeted Platform(s)
Android
Contributor guide
Research direction
Start by tracing FirebaseApp.DefaultInstance through FirebaseApp.CreateAndTrack() and FirebaseInitProvider on Android, then compare that initialization path with CheckAndFixDependenciesAsync(). Done means a Play-Services-dependent product can fail without causing unrelated Firebase products to throw, with the proposed FirebaseApp.IsComponentAvailable(Type) API supporting per-product checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, csharp, firebase, unity
- Domain
- api, mobile
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100