firebase / firebase/firebase-ios-sdk

[FR]: Support full Firebase teardown on background + reconfigure on foreground (audio / long-running background apps)

Open
#16,300 0 comments 1 reaction 0 assignees View on GitHub
api: core type: feature request
Dominant language
C++
Stars
6.7k
Forks
1.8k
Avg merge
2d 18h
Merged PRs (30d)
75

Description

### Description

## Summary

Firebase on iOS is not one network client — it is several products that all initialize from a single `FirebaseApp.configure()` and then run their own timers, retries, uploads, and disk-backed queues. There is no supported “pause all Firebase activity while backgrounded” API, and no supported way to fully tear down and later re-initialize the SDK during a single app session.

For apps with `UIBackgroundModes: audio`, this is especially painful. Most apps suspend shortly after backgrounding, so Firebase background traffic is short-lived. An audio-background app can remain alive for **hours**. Any recurring Firebase work that is negligible over 30 seconds becomes a meaningful **energy, thermal, and battery cost** over an entire night.

We do **not** need fine-grained pause/resume. **Full teardown on `didEnterBackground` and re-`configure()` on `didBecomeActive` would be sufficient.** The problem is that Firebase iOS does not support that cleanly either.

---

## Description

Apps that legitimately run in the background for long periods (e.g. `UIBackgroundModes: audio` for overnight playback) need a supported way to:

- **Stop all outbound Firebase networking**
- **Stop long-running and recurring background work** (timers, token refresh, upload schedulers, gRPC keepalives, `UIApplication` background tasks)
- **Avoid unnecessary disk I/O** (GDT batch files, analytics queues, Firestore persistence/cache writes)

…then restore Firebase when the user returns to the foreground.

Today there is no single API. Each Firebase product has separate (or no) controls, uses mixed transports (gRPC + URLSession), and shares infrastructure (Installations, GoogleDataTransport) that continues scheduling work independently.

We have tried per-product mitigations (`Firestore.disableNetwork()`, App Check provider swap, listener removal, app-level file I/O gating) but background traffic, recurring work, and disk activity continue from Installations, GDT/Analytics, Auth, Firestore persistence, and gRPC paths we cannot intercept.

`FirebaseApp.delete()` exists but is undocumented for lifecycle use, does not coordinate product teardown (e.g. Firestore `terminate()`), and does not guarantee all background schedulers, upload queues, or disk-backed pipelines stop. We need a **single supported `teardown()` that guarantees zero outbound Firebase traffic and no further Firebase-driven background work**, and a **safe path to `configure()` again** when the user returns.

---

## Why “just deinit” doesn’t work today

`FirebaseApp.delete()` exists, but it is not a lifecycle API:

```swift
// Theoretical — not a supported pattern
await FirebaseApp.app()?.delete()
FirebaseApp.configure() // only works if delete fully cleared state
```

**Blockers:**

1. **`configure()` is one-shot by design** — calling it twice throws (`Default app has already been configured`). You must `delete()` first, and even then reconfigure is undocumented and fragile ([#13132](https://github.com/firebase/firebase-ios-sdk/issues/13132), [quickstart-ios #960](https://github.com/firebase/quickstart-ios/issues/960)).

2. **`delete()` ≠ “no more network / no more work”** — it clears the Core container and posts `FIRAppDeleteNotification`, but product SDKs may still have:
- Firestore gRPC connections (needs separate `terminate()`)
- GoogleDataTransport upload queues / background tasks
- Auth / App Check timers already scheduled
- Disk-backed batch files still being read/written

3. **No documented teardown sequence** — there is no official recipe like:
```
Firestore.terminate() → stop GDT → cancel App Check/Auth timers → FirebaseApp.delete()
```
Each product owns its own resources; `delete()` does not coordinate them.

4. **Reconfigure is messy** — Google has said delete+reconfigure is “not cleanly possible” (Analytics loss, new FCM/Installations IDs, listeners need full rebuild). Acceptable for our use case, but not something Firebase treats as supported.

5. **App code holds singletons** — services assume Firebase is always alive. A real deinit means invalidating every listener/reference and rebuilding on foreground. Workable in app code, but Firebase does not make the SDK side clean.

---

## Firebase background activity by layer

| Layer | What it does | Can you stop it today? |
|--------|----------------|------------------------|
| **Firebase Installations (FIS)** | Creates/syncs installation ID on configure; shared by Analytics, App Check, etc. | **No pause API.** Only delay `configure()` or `Installations.delete()` (nuclear). [#15513](https://github.com/firebase/firebase-ios-sdk/issues/15513) |
| **Firestore** | Long-lived **gRPC** stream to `firestore.googleapis.com`; local persistence | Partially: `disableNetwork()` + remove listeners. Not visible to `URLProtocol`. gRPC and cache I/O may continue. |
| **Auth / Functions / Storage** | **URLSession**-based HTTP | No global pause. Auth refreshes tokens on its own schedule. |
| **App Check** | Auto-refreshes at ~half token TTL | `isTokenAutoRefreshEnabled = false` helps, but other Firebase SDKs can still trigger token fetches. |
| **Analytics** | Batches via **GoogleDataTransport (GDT)** | No runtime pause. GDT uses `beginBackgroundTask`, disk queues, and uploads to measurement endpoints. [#13689](https://github.com/firebase/firebase-ios-sdk/issues/13689), [#14935](https://github.com/firebase/firebase-ios-sdk/issues/14935) |

**Why per-product mitigations are insufficient:**

1. **gRPC is invisible** to app-level HTTP interceptors — Firestore won’t appear in `URLProtocol` logging.
2. **Per-product controls don’t compose** — stopping Firestore doesn’t stop FIS, Analytics/GDT, or Auth.
3. **SDK internals bypass app-level hooks** — App Check provider swaps may not affect tokens already requested by other Firebase modules; GDT runs on its own queue/background tasks.
4. **`disableNetwork()` ≠ “no activity”** — it stops Firestore RPCs from the app’s perspective, but doesn’t tear down the whole client; local persistence may continue; `terminate()` is destructive and unsafe ad hoc ([#15463](https://github.com/firebase/firebase-ios-sdk/issues/15463)).
5. **Info.plist collection flags don’t stop core plumbing** — flags like `GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED = false` don’t disable Installations or all uploads.

---

## Environment

- **Platform:** iOS (SwiftUI)
- **Firebase iOS SDK:** 12.11.0 (SPM)
- **Products in use:** FirebaseCore, FirebaseAuth, FirebaseFirestore, FirebaseFunctions, FirebaseStorage, FirebaseAnalytics, FirebaseAppCheck
- **Background mode:** `audio` (app process stays alive for hours)
- **Xcode:** 26.3
- **iOS:** 26.5

---

## Use case

Sleep/ambient audio app: user starts playback and backgrounds the phone overnight. The app should:

- Keep **local audio** running
- **Not** perform Firebase sync, token refresh, analytics upload, or App Check attestation while backgrounded
- **Not** run recurring Firebase timers or long-lived background tasks overnight
- **Not** perform avoidable Firebase-related disk I/O (event batching, cache maintenance, persistence writes) while backgrounded
- Restore Firebase when the user returns to foreground

Unlike typical apps that suspend shortly after backgrounding, an audio-background app can remain alive for **many hours**. Any recurring Firebase work that would be negligible over 30 seconds becomes a meaningful **energy and thermal cost** over an entire night.

---

## Expected behavior

A documented, supported lifecycle API such as:

```swift
// Pseudocode — exact shape flexible
await Firebase.teardown() // stops networking, timers, uploads, and product background work
// … hours of audio-only background …
await FirebaseApp.configure() // clean re-init
```

That would:

1. Stop scheduled/periodic uploads (Analytics/GDT, Installations maintenance)
2. Terminate Firestore gRPC sync and stop further Firestore-driven background work
3. Cancel Auth / App Check token refresh and other recurring timers
4. Stop or flush-and-halt disk-backed pipelines (GDT batch files, analytics queues, etc.) rather than letting them continue reading/writing in the background
5. End any Firebase-owned `UIApplication` background tasks promptly
6. Guarantee **no outbound Firebase traffic** and **no further Firebase-scheduled work** after `teardown()` completes
7. Allow safe `configure()` again on foreground (idempotent, no crash)
8. Be observable (callback/log hook) for debugging in Instruments Energy Log

An optional helper tied to `UIApplication` background/active notifications would also be welcome:

```swift
// Pseudocode
FirebaseLifecycle.bind(to: UIApplication.shared)
```

---

## Actual behavior

Despite app-level mitigations, background network activity, recurring work, and disk I/O from Firebase subsystems continue.

### Mitigations already implemented (insufficient)

On `UIApplication.didEnterBackground` we:

```swift
try await Firestore.firestore().disableNetwork()
// Remove all Firestore snapshot listeners across services
AppCheck.appCheck().isTokenAutoRefreshEnabled = false
// Swap AppCheckProviderFactory to a no-op provider
// Optional URLProtocol block for firebaseappcheck.googleapis.com
// App-level gate blocking non-audio file I/O (BackgroundFileIO)
```

On `UIApplication.didBecomeActive` we reverse the above (`enableNetwork()`, restore App Check provider, reattach listeners, etc.).

Even with app-level file I/O blocked, Firebase products still perform their own persistence and queue management internally.

### Observed while backgrounded

#### Networking

- `firestore.googleapis.com` (gRPC — not visible to URLProtocol)
- `firebaseinstallations.googleapis.com`
- `firebaseappcheck.googleapis.com`
- `securetoken.googleapis.com` (Auth)
- `app-measurement.com` / `region1.app-analytics-services.com` (Analytics via GDT)

#### Long-running / recurring work

- `GDTCCTUploader-upload` background tasks running for 30+ seconds ([#13689](https://github.com/firebase/firebase-ios-sdk/issues/13689), [#14935](https://github.com/firebase/firebase-ios-sdk/issues/14935))
- Periodic App Check token refresh timers
- Auth / Installations maintenance retries
- Firestore gRPC keepalive / reconnect behavior

#### File I/O / energy impact

- GDT event batching reading/writing local queue files
- Firestore local persistence / cache activity even when network is disabled
- Repeated wakeups from recurring timers causing CPU + storage energy use over multi-hour background sessions

---

## Steps to reproduce

1. Integrate FirebaseAuth, Firestore, Functions, Storage, Analytics, AppCheck
2. Enable `UIBackgroundModes: audio`
3. Start long-running audio playback
4. Background the app for 30+ minutes (ideally several hours)
5. Monitor with:
- **Instruments → Energy Log**
- **Instruments → Network**
- **Console** (`GDTCCTUploader-upload` warnings)
- Optional **URLProtocol** logger for HTTP traffic (note: will not capture Firestore gRPC)
6. Apply mitigations:
- `Firestore.disableNetwork()`
- `AppCheck.appCheck().isTokenAutoRefreshEnabled = false`
- Swap App Check provider to no-op
- Remove all Firestore listeners
- Block non-audio app file I/O

**Result:** Firebase-related network activity, recurring background work, and/or disk I/O still occur.

---

## Why current per-product APIs are inadequate

| Component | Existing control | Gap |
|-----------|------------------|-----|
| Firestore | `disableNetwork()` | gRPC; doesn't affect other products; not designed for lifecycle; local persistence may continue |
| Firestore | `terminate()` | Destructive, not coordinated with other products; unsafe to use ad hoc in production ([#15463](https://github.com/firebase/firebase-ios-sdk/issues/15463)) |
| App Check | `isTokenAutoRefreshEnabled` | Other SDKs may still request tokens; attestation can still run |
| Analytics | `Analytics.setAnalyticsCollectionEnabled(false)` | Must be set before events; GDT may still flush queued batches from disk |
| Installations | None | Required on `configure()`; no pause ([#15513](https://github.com/firebase/firebase-ios-sdk/issues/15513)) |
| GoogleDataTransport | None | Shared upload pipeline; recurring uploads; disk-backed queues; background tasks not app-controllable |
| FirebaseCore | `FirebaseApp.delete()` | Undocumented for lifecycle; doesn't coordinate product teardown; re-`configure()` is fragile ([#13132](https://github.com/firebase/firebase-ios-sdk/issues/13132), [quickstart-ios #960](https://github.com/firebase/quickstart-ios/issues/960)) |

There is also no way to intercept **gRPC** traffic at the app level (unlike URLSession), and no supported way to stop Firebase's internal disk-backed pipelines without tearing down the whole SDK.

---

## Impact

- **Energy usage / battery drain** — recurring timers, uploads, reconnect loops, and disk I/O over multi-hour background audio sessions
- **Thermal cost** — unnecessary wakeups while the app should be nearly idle aside from audio playback
- **Privacy / data minimization** — analytics and token traffic when the user isn't interacting
- **Debugging difficulty** — work originates from multiple internal schedulers and persistence layers with no unified lifecycle hook; hard to answer “who triggered this request/task/write?”
- **Fragile workarounds** — custom App Check providers, URLProtocol hacks, and per-listener teardown that do not address Firebase's internal recurring tasks or file I/O
- **Production risk** — `terminate()` and `delete()` exist but are not safe, documented lifecycle tools

---

## Proposed solution

### Minimum acceptable fix

A documented, supported lifecycle teardown — **not** necessarily sophisticated pause/resume semantics.

### Specific requests

1. **`FirebaseApp.teardown()`** (or equivalent) that:
- Terminates Firestore
- Stops GDT uploads and halts disk-backed event pipelines
- Cancels App Check / Auth / Installations timers and retries
- Ends Firebase-owned background tasks
- Awaits completion (async) before returning
- Documents that **no Firebase network activity, recurring work, or further SDK-driven file I/O** will occur after it returns

2. **Safe re-`configure()`** after teardown:
- Idempotent, no crash
- Clear contract for what state is lost vs persisted locally
- Guidance for apps that need to rebuild listeners/singletons on foreground

3. **Optional lifecycle helper** — e.g. `FirebaseLifecycle.bind(to: UIApplication)` for background teardown / foreground configure

4. **Debug logging / assertions** — log or assert if any Firebase URLSession, gRPC, timer, or disk-queue activity occurs after `teardown()` completes

5. **Optional Info.plist default** for audio-background apps — e.g. `FirebaseTeardownInBackground = true`

---

## Related issues

- [#15513](https://github.com/firebase/firebase-ios-sdk/issues/15513) — Installations network on configure
- [#13689](https://github.com/firebase/firebase-ios-sdk/issues/13689) — GDT background task warnings
- [#14935](https://github.com/firebase/firebase-ios-sdk/issues/14935) — GDT uploader background tasks
- [#13132](https://github.com/firebase/firebase-ios-sdk/issues/13132) — `configure()` throws if called twice
- [#15463](https://github.com/firebase/firebase-ios-sdk/issues/15463) — Firestore `terminate()` crashes on subsequent access
- [quickstart-ios #960](https://github.com/firebase/quickstart-ios/issues/960) — delete + reconfigure not cleanly supported
- [#12177](https://github.com/firebase/firebase-ios-sdk/issues/12177) — granular Performance instrumentation (same “all or nothing” pattern)
- [#11060](https://github.com/firebase/firebase-ios-sdk/issues/11060) — disabling Performance instrumentation granularly

---

## Additional notes for maintainers

This request is motivated by **energy usage**, not just correctness. For overnight audio apps, Firebase’s always-on, self-healing sync model (tokens, installations, analytics batches, App Check attestation, persistence) conflicts with the expectation that the process should be almost entirely idle except for audio playback.

We are happy to provide Instruments traces (Energy Log + Network) and Console logs from a multi-hour background session if that would help reproduce or prioritize this.

### API Proposal

_No response_

### Firebase Product(s)

Analytics

Contributor guide

Open the contributing guide

Research direction

Start with the FirebaseApp.configure() and delete() lifecycle entry points and the product controls named in the issue. Reproduce the background behavior with Instruments Energy Log, Network, and Console, then define a coordinated teardown and foreground reconfiguration whose completion guarantees no Firebase traffic or scheduled work.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
mobile-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.