Skip to content

[firebase_messaging][iOS] 16.7.0 breaks foreground notifications posted by other plugins on UIScene apps that own the UNUserNotificationCenter delegate #18699

Description

@YuuWoods

Is there an existing issue for this?

  • I have searched the existing issues.

Which plugins are affected?

Messaging

Which platforms are affected?

iOS

Description

After upgrading firebase_messaging from 16.5.0 to 16.7.0, foreground notifications posted by flutter_local_notifications are no longer displayed on iOS.

This affects apps that:

  1. adopt the UIScene lifecycle (UIApplicationSceneManifest + FlutterImplicitEngineDelegate),
  2. assign UNUserNotificationCenter.current().delegate themselves (so that FlutterAppDelegate forwards willPresentNotification to every plugin registered via addApplicationDelegate), and
  3. render foreground notifications themselves from data-only messages, without calling setForegroundNotificationPresentationOptions.

FlutterLocalNotificationsPlugin.show() completes normally, but nothing appears on screen.

This is a regression: 16.5.0 works, 16.7.0 does not. 16.6.0 contains no iOS notification changes.

Calling FLTFirebaseMessagingPlugin.configureNotificationCenterDelegate() as documented for UIScene apps does not help either (see the verification table below).

Root cause

#18620 added an early setup call in registerWithRegistrar:

if ([UIApplication sharedApplication].connectedScenes.count > 0) {
  instance->_sceneDidConnect = YES;
  [instance setupNotificationHandlingWithRemoteNotification:nil];
}

The intent was to request APNs registration when UIScene apps register the plugin after the launch callbacks have already fired. However, setupNotificationHandlingWithRemoteNotification: also calls configureNotificationCenterDelegate, which contains this guard:

if (currentDelegate != nil) {
  if ([currentDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
    shouldReplaceDelegate = NO;
  }
}

The guard only applies when a delegate is already installed. Depending on when the scene connects, plugin registration can run before the host assigns its delegate, so currentDelegate is nil, the guard is skipped, and this plugin takes over the delegate while storing nil in _originalNotificationCenterDelegate.

The host then overwrites the delegate, but this plugin remains in the forwarding chain via addApplicationDelegate. When a local notification is about to be presented, its willPresentNotification runs and reaches:

} else {
  UNNotificationPresentationOptions presentationOptions = UNNotificationPresentationOptionNone;
  NSDictionary *persistedOptions = [[NSUserDefaults standardUserDefaults]
      dictionaryForKey:kMessagingPresentationOptionsUserDefaults];
  if (persistedOptions != nil) { ... }
  completionHandler(presentationOptions);
}

_originalNotificationCenterDelegate is nil and setForegroundNotificationPresentationOptions was never called, so it answers UNNotificationPresentationOptionNone and suppresses a notification that belongs to another plugin.

Reproducing the issue

Applied to firebase_messaging/example on main.

ios/Runner/AppDelegate.swift — keep the documented call and additionally let the host own the delegate:

   override func application(
     _ application: UIApplication,
     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
   ) -> Bool {
     // UIScene registers plugins after this method returns. Apple requires the
     // UNUserNotificationCenter delegate to be set before launch completes.
     FLTFirebaseMessagingPlugin.configureNotificationCenterDelegate()
+
+    // Apps that also use flutter_local_notifications must own the delegate so
+    // that FlutterAppDelegate forwards willPresentNotification to every plugin
+    // registered through addApplicationDelegate.
+    UNUserNotificationCenter.current().delegate = self
+
     return super.application(application, didFinishLaunchingWithOptions: launchOptions)
   }

lib/main.dart — initialize() is required before any notification can be shown on iOS, and the presentation options are removed:

-  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
-    alert: true,
-    badge: true,
-    sound: true,
-  );
+  // Required before any notification can be shown on iOS.
+  await flutterLocalNotificationsPlugin.initialize(
+    settings: const InitializationSettings(
+      android: AndroidInitializationSettings('launch_background'),
+      iOS: DarwinInitializationSettings(),
+    ),
+  );
+
+  // NOTE: setForegroundNotificationPresentationOptions is intentionally NOT
+  // called. Apps that render foreground notifications themselves with
+  // flutter_local_notifications do not need FCM to present anything.

Also make showFlutterNotification read title/body from message.data so it posts a notification for data-only messages on iOS.

Then send a data-only message while the app is in the foreground:

{
  "message": {
    "token": "<fcm-token>",
    "data": { "title": "title", "body": "body" }
  }
}

Result on 16.7.0

test: delegate after configureNotificationCenterDelegate = Optional(<FLTFirebaseMessagingPlugin: 0x30154ce00>)
test: delegate after didFinishLaunching = Optional(<Runner.AppDelegate: 0x301d44910>)
test: delegate before plugin registration = Optional(<Runner.AppDelegate: 0x301d44910>)
test: delegate after plugin registration = Optional(<Runner.AppDelegate: 0x301d44910>)

test: onMessage received (1789709030567108)
test: localNotification.show start
test: localNotification.show done

show() completes, but no notification is displayed.

Proposed fix

Request APNs registration without performing the delegate handover. registerForRemoteNotificationsIfAutoInitEnabled was added in #18650 and is documented as safe to call repeatedly:

     if ([UIApplication sharedApplication].connectedScenes.count > 0) {
       instance->_sceneDidConnect = YES;
-      [instance setupNotificationHandlingWithRemoteNotification:nil];
+      [instance registerForRemoteNotificationsIfAutoInitEnabled];
     }

Full notification handling then runs from didFinishLaunching / scene:willConnect as before, by which time the host delegate is installed and the FlutterAppLifeCycleProvider guard applies.

Verification

Same device, same message, using the example described above:

configureNotificationCenterDelegate() plugin install foreground notification APNs token
not called 16.7.0 as released existing not displayed received
not called 16.7.0 + patch existing displayed received
called 16.7.0 as released existing not displayed received
called 16.7.0 + patch existing displayed received
called 16.7.0 + patch fresh (app deleted first) displayed received

show() completed in every run; the only difference is the delegate path on the iOS side.

The last row matches the test plan of #18620 (app deleted from a physical device, no cached APNs token). getToken() resolved without apns-token-not-set, so the goal of #18620 — fixing #18555 — is preserved.

Firebase Core version

4.15.0

Flutter Version

3.44.9

Relevant Log Output

test: delegate after configureNotificationCenterDelegate = Optional(<FLTFirebaseMessagingPlugin: 0x30154ce00>)
test: delegate after didFinishLaunching = Optional(<Runner.AppDelegate: 0x301d44910>)
test: delegate before plugin registration = Optional(<Runner.AppDelegate: 0x301d44910>)
test: delegate after plugin registration = Optional(<Runner.AppDelegate: 0x301d44910>)
test: onMessage received (1789709030567108)
test: localNotification.show start
test: localNotification.show done

Flutter dependencies

firebase_core: ^4.15.0
firebase_messaging: ^16.7.0
flutter_local_notifications: ^21.0.0

Additional context and comments

Tested on iPhone SE (3rd generation), iOS 18.0.1, Flutter 3.44.9.

The same result occurs when FLTFirebaseMessagingPlugin.configureNotificationCenterDelegate() is not called and only UNUserNotificationCenter.current().delegate = self is set.

Activity

  1. changed the title [-][firebase_messaging]: 16.7.0 breaks foreground notifications posted by other plugins on UIScene apps that own the UNUserNotificationCenter delegate[/-] [+][firebase_messaging][iOS] 16.7.0 breaks foreground notifications posted by other plugins on UIScene apps that own the UNUserNotificationCenter delegate[/+] on Sep 18, 2026
  2. danilz-276 commented on Sep 21, 2026

    @danilz-276

    I'm facing the same issue. Have you found a solution yet?

    Working

    firebase_core: 4.14.0
    firebase_crashlytics: 5.3.0
    firebase_messaging: 16.6.0

    Not working

    firebase_core: ^4.15.0
    firebase_crashlytics: ^5.4.0
    firebase_messaging: ^16.7.0
  3. SelaseKay commented on Sep 22, 2026

    @SelaseKay
    Contributor

    Hi @YuuWoods, thanks for the report. I'm looking into this.

  4. SelaseKay commented on Sep 22, 2026

    @SelaseKay
    Contributor

    Hi @YuuWoods, thanks again for the reproduction. There is a draft fix in #18701.

    willPresentNotification was calling the shared completion handler with UNNotificationPresentationOptionNone whenever AppDelegate owned the notification center. That hid the local banner even though flutter_local_notifications show() had already completed. The fix leaves that handler alone for a forwarded local notification. APNs registration from 16.7.0 is unchanged.

    Please try the same case as before: UIScene, UNUserNotificationCenter.delegate set to AppDelegate, no setForegroundNotificationPresentationOptions, data-only message in the foreground, local notification posted from onMessage.

    dependency_overrides:
      firebase_messaging:
        git:
          url: https://github.com/firebase/flutterfire.git
          ref: messaging_18699
          path: packages/firebase_messaging/firebase_messaging

    Then flutter pub get, reinstall the iOS app, and send the data-only message again.

  5. added
    blocked: customer-responseWaiting for customer response, e.g. more information was requested.
    and removed
    Needs AttentionThis issue needs maintainer attention.
    on Sep 22, 2026
  6. YuuWoods commented on Sep 24, 2026

    @YuuWoods
    Author

    Thanks for the quick fix. I verified messaging_18699 and the foreground banner is displayed again.

    In firebase_messaging/example, with the same setup as the reproduction above — UIScene, UNUserNotificationCenter.delegate set to AppDelegate, no setForegroundNotificationPresentationOptions, data-only message posted as a local notification from onMessage — on a fresh install:

    • Before the fix: show() completed but no banner appeared.
    • With this branch: the banner appears once.

    getToken() resolved without apns-token-not-set on the fresh install, so APNs registration is unaffected.

    I also confirmed the same on our production app with dependency_overrides pointing at the branch.

    Device: iPhone SE (3rd generation), iOS 18.0.1, Flutter 3.44.9.

  7. added
    Needs AttentionThis issue needs maintainer attention.
    and removed
    blocked: customer-responseWaiting for customer response, e.g. more information was requested.
    on Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions