Is there an existing issue for this?
Which plugins are affected?
Analytics
Which platforms are affected?
iOS
Description
After migrating the iOS host app to the UIScene lifecycle, firebase_analytics stops
recording the firebase_campaign event when the app is opened from a deep link or universal
link. Campaign attribution is silently lost, while every other Analytics event continues to work
normally.
Expected: opening the app from a link carrying campaign parameters (e.g.
?utm_source=...&utm_medium=...&utm_campaign=...) records firebase_campaign, as it did before
the UIScene migration.
Actual: firebase_campaign is never recorded — neither on cold start nor when the app is
resumed from background. There is no error, no warning and no log output of any kind; the deep
links themselves keep working, so only the attribution data disappears.
Reverting the UIScene adoption (removing UIApplicationSceneManifest from Info.plist) restores
the event.
This makes the failure particularly costly: nothing surfaces at runtime, and because
firebase_campaign is only observable in BigQuery (see below), the loss can go unnoticed for a
long time after release. With Apple moving toward mandatory UIScene adoption, this will affect a
growing number of Flutter apps.
Reproducing the issue
- Use the
firebase_analytics example app (or any Flutter app with firebase_analytics) on
Flutter > 3.44.0.
- Adopt the UIScene lifecycle in the iOS host app — add
UIApplicationSceneManifest to
Info.plist and a SceneDelegate: FlutterSceneDelegate, per Flutter's
UISceneDelegate migration guide.
- Open the app from a link carrying campaign parameters — once on cold start and once with
the app in background:
xcrun simctl openurl booted "https://<your-universal-link-domain>/?utm_source=test&utm_medium=test&utm_campaign=uiscene"
- Verify in the BigQuery export:
SELECT
event_timestamp,
event_name,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') AS source,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') AS medium,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'campaign') AS campaign
FROM `<project>.analytics_<property_id>.events_*`
WHERE event_name = 'firebase_campaign'
AND _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 3 DAY))
ORDER BY event_timestamp DESC
- Result: no
firebase_campaign rows for the UIScene build. The same test on a build
without UIApplicationSceneManifest produces the event as expected.
⚠️ Note on verification: firebase_campaign is not surfaced in Analytics DebugView
by design — it only appears in the BigQuery export. DebugView cannot be used to confirm or
deny this bug. Mind the BQ export latency when reproducing (daily batch, unless streaming
export is enabled).
Firebase Core version
4.0.0
Flutter Version
3.44.0
Relevant Log Output
No log output is produced — this is part of the problem.
Analytics emits no warning or error; all other events continue to be recorded,
and only firebase_campaign is missing from the BigQuery export.
Flutter dependencies
Expand Flutter dependencies snippet
# Trimmed to the relevant packages (host app is a large monorepo).
firebase_core: 4.0.0
firebase_core_platform_interface: 6.0.0
firebase_analytics: 12.0.0
firebase_analytics_platform_interface: 5.0.0
# iOS native (Podfile.lock)
FirebaseAnalytics: 12.0.0
FirebaseCore: 12.0.0
GoogleUtilities: ~> 8.1
Additional context and comments
I've traced it to how Analytics hooks into the iOS app lifecycle in order to find the root cause and try a workaround, let me know if this is correct:
Firebase Analytics records firebase_campaign by calling FIRAnalytics.handleOpen(_:)
automatically, via swizzling of the UIApplicationDelegate — and only that. It does not
swizzle UISceneDelegate.
Verified against the installed binary (FirebaseAnalytics / GoogleAppMeasurement 12.0.0):
$ nm -u GoogleAppMeasurement.framework/GoogleAppMeasurement | grep -i GUL
_OBJC_CLASS_$_GULAppDelegateSwizzler # ← linked
_OBJC_CLASS_$_GULAppEnvironmentUtil
_OBJC_CLASS_$_GULSwizzler
# _OBJC_CLASS_$_GULSceneDelegateSwizzler → NOT linked
The only lifecycle selectors it references are UIApplicationDelegate ones — no
scene:openURLContexts::
$ strings GoogleAppMeasurement | grep -iE "continueUserActivity|handleOpenURL|openURLContexts"
application:continueUserActivity:restorationHandler:
application:handleOpenURL:
handleOpenURL:clickTimestampInMs:eventTimestampInSeconds:
Under UIScene, UIKit delivers URL opens to scene(_:openURLContexts:), scene(_:continue:) and
scene(_:willConnectTo:options:), and never calls the AppDelegate equivalents — so the
swizzled implementation is never reached and handleOpen(_:) is never invoked.
This is documented native behaviour, not a native bug —
FIRAnalytics(AppDelegate):
Analytics does not require delegation implementation from the AppDelegate if neither
SwiftUI nor UIScene lifecycle is adopted.
…and it specifies the manual calls required under UIScene:
| UIScene callback |
Required call |
scene(_:willConnectTo:options:) |
handleOpen(_:) and handleUserActivity(_:) |
scene(_:openURLContexts:) |
handleOpen(_:) |
scene(_:continue:) |
handleUserActivity(_:) |
The firebase_analytics iOS plugin never calls handleOpen/handleUserActivity — it relies
entirely on the native SDK's AppDelegate swizzling, which UIScene makes unreachable. There is
no scene-lifecycle handling in the plugin at all:
$ grep -rn "addSceneDelegate\|FlutterSceneLifeCycleDelegate\|openURLContexts\|handleOpen" \
firebase_analytics-12.0.0/ios/
# (no matches)
Since Flutter 3.38, plugins can register a scene delegate via registrar.addSceneDelegate(_:)
(FlutterPlugin.h) implementing FlutterSceneLifeCycleDelegate — the mechanism described in
Flutter's
UISceneDelegate plugin migration guide.
Related UIScene gaps already addressed in other plugins: #18054 / #18383 (Auth), #18555
(Messaging). Analytics appears to have been missed.
Notes
GoogleUtilities does ship a GULSceneDelegateSwizzler, but Analytics doesn't link it — and
it only covers scene:openURLContexts:, not scene:continue: (universal links) or
willConnectTo (cold start), so enabling it alone would not be sufficient.
- The failure is easy to miss: deep links keep working (the plugin ecosystem handles scene
events), so only campaign telemetry breaks — with no diagnostic, and no visibility in
DebugView since firebase_campaign is BigQuery-only.
Is there an existing issue for this?
Which plugins are affected?
Analytics
Which platforms are affected?
iOS
Description
After migrating the iOS host app to the UIScene lifecycle,
firebase_analyticsstopsrecording the
firebase_campaignevent when the app is opened from a deep link or universallink. Campaign attribution is silently lost, while every other Analytics event continues to work
normally.
Expected: opening the app from a link carrying campaign parameters (e.g.
?utm_source=...&utm_medium=...&utm_campaign=...) recordsfirebase_campaign, as it did beforethe UIScene migration.
Actual:
firebase_campaignis never recorded — neither on cold start nor when the app isresumed from background. There is no error, no warning and no log output of any kind; the deep
links themselves keep working, so only the attribution data disappears.
Reverting the UIScene adoption (removing
UIApplicationSceneManifestfromInfo.plist) restoresthe event.
This makes the failure particularly costly: nothing surfaces at runtime, and because
firebase_campaignis only observable in BigQuery (see below), the loss can go unnoticed for along time after release. With Apple moving toward mandatory UIScene adoption, this will affect a
growing number of Flutter apps.
Reproducing the issue
firebase_analyticsexample app (or any Flutter app withfirebase_analytics) onFlutter > 3.44.0.
UIApplicationSceneManifesttoInfo.plistand aSceneDelegate: FlutterSceneDelegate, per Flutter'sUISceneDelegate migration guide.
the app in background:
firebase_campaignrows for the UIScene build. The same test on a buildwithout
UIApplicationSceneManifestproduces the event as expected.Firebase Core version
4.0.0
Flutter Version
3.44.0
Relevant Log Output
Flutter dependencies
Expand
Flutter dependenciessnippetAdditional context and comments
I've traced it to how Analytics hooks into the iOS app lifecycle in order to find the root cause and try a workaround, let me know if this is correct:
Firebase Analytics records
firebase_campaignby callingFIRAnalytics.handleOpen(_:)automatically, via swizzling of the
UIApplicationDelegate— and only that. It does notswizzle
UISceneDelegate.Verified against the installed binary (
FirebaseAnalytics/GoogleAppMeasurement12.0.0):The only lifecycle selectors it references are
UIApplicationDelegateones — noscene:openURLContexts::Under UIScene, UIKit delivers URL opens to
scene(_:openURLContexts:),scene(_:continue:)andscene(_:willConnectTo:options:), and never calls theAppDelegateequivalents — so theswizzled implementation is never reached and
handleOpen(_:)is never invoked.This is documented native behaviour, not a native bug —
FIRAnalytics(AppDelegate):…and it specifies the manual calls required under UIScene:
scene(_:willConnectTo:options:)handleOpen(_:)andhandleUserActivity(_:)scene(_:openURLContexts:)handleOpen(_:)scene(_:continue:)handleUserActivity(_:)The
firebase_analyticsiOS plugin never callshandleOpen/handleUserActivity— it reliesentirely on the native SDK's
AppDelegateswizzling, which UIScene makes unreachable. There isno scene-lifecycle handling in the plugin at all:
Since Flutter 3.38, plugins can register a scene delegate via
registrar.addSceneDelegate(_:)(
FlutterPlugin.h) implementingFlutterSceneLifeCycleDelegate— the mechanism described inFlutter's
UISceneDelegate plugin migration guide.
Related UIScene gaps already addressed in other plugins: #18054 / #18383 (Auth), #18555
(Messaging). Analytics appears to have been missed.
Notes
GoogleUtilitiesdoes ship aGULSceneDelegateSwizzler, but Analytics doesn't link it — andit only covers
scene:openURLContexts:, notscene:continue:(universal links) orwillConnectTo(cold start), so enabling it alone would not be sufficient.events), so only campaign telemetry breaks — with no diagnostic, and no visibility in
DebugView since
firebase_campaignis BigQuery-only.