In a Flutter chat app using FCM data-only pushes with awesome_notifications for local notifications, FirebaseMessaging.onBackgroundMessage works perfectly on Android (all states) and iOS debug (foreground/background), but fails silently in iOS release when the app is backgrounded/killed.
Setup (verified working in debug) main_bootstrap.dart:
dart
await Firebase.initializeApp(options: firebaseOptions);
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); // Top-level handler with @pragma('vm:entry-point')
runApp(ProviderScope(child: MainApp()));
Xcode Capabilities (Release target):
✅ Push Notifications
✅ Background Modes → Remote notifications + Background fetch
FCM Payload (data-only, silent):
json
{
"tokens": ["device_token"],
"data": {
"roomId": "123",
"roomType": "group",
"senderName": "Ahmed",
"preview": "Hello there",
"timestamp": "1735400000000"
},
"apns": {
"payload": { "aps": { "content-available": 1 } },
"headers": { "apns-push-type": "background", "apns-priority": "5" }
}
}
Minimal test handler (still fails in iOS release background):
dart
@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('🔥 BACKGROUND: ${message.messageId}'); // Never appears in Xcode logs
}
Questions: Is this an iOS release-specific limitation where silent pushes are throttled/dropped more aggressively than debug, even with correct content-available:1 + apns-push-type:background?
firebase_messaging version issue? Using latest firebase_messaging: ^15.0.0 - any known release build bugs?
What Xcode build settings differ between debug/release that could block background handler invocation?
Workaround for chat apps? Should I send dual payloads (silent for background handler + visible notification as fallback) or is there a reliable release pattern?
This is a hard space to get a good handle on.
Using Flutter makes it harder because it tries to abstract a lot of the platform specific stuff away, but it still uses platform services.
When you use FCM to message an iOS device, the delivery to the device and to your app are managed by Apples APNS (or it's sometimes called APS). So, server side, you can talk to FCM whether you're messaging Android or iOS. But when you're messaging an iOS device FCM hands it over to Apple's servers to deliver to their platform.
iOS handles push messaging significantly differently from Android, particularly the silent / data only push messaging.
Also, on iOS there's a significant difference between having your app backgrounded and swiped closed:
If your app is backgrounded, messages are still delivered to the app.
If your app is swiped closed, messages are not delivered to your iOS app.
This is different to Android. On Android if you swipe the app closed, push messages are delivered to the app. (If you force kill the app, messages are not delivered).
When the user next launches the app on iOS, the silent push message is delivered.
I think the icon badge does get updated.
The main differences we have found are:
silent push messages (data only) are significantly de-prioritised on iOS. Where a notification message is usually delivered almost immediately, sometimes it can take much longer for silent / data only push messages.
Only one silent message is queued for delivery at a time. If you try to send an other data-only message before the first one is delivered to the app, the first one is discarded and only the latest is queued for delivery.
It's also an area where the terminology is dangerously overloaded. Apple calls their messaging "Notifications" even if there is no Notification element - if it's silent / data only, they still call it a "Notification". And there is a huge difference between what you can do with a locally created notification, vs what you can do with an FCM / remote notification. But the documentation on these two things just say "notification" and often don't distinguish between a local (awesome_notifications) notification and a remote (FCM) notification, which just adds another layer of complexity.
This is Apple's documentation on what happens once FCM passes the message over to APNS for processing.
https://developer.apple.com/documentation/usernotifications/pushing-background-updates-to-your-app
For our use case (which is not a chat app) we are moving to always sending a data+notification message. Because it has that notification element it's more reliably delivered, particularly on iOS.
That would probably be what I'd do for a chat app also, given that if your app is foreground the system notification is not created, even if you specify one. Whereas if it's backgrounded or swiped closed, you still get that system notification (just not the silent message).