flutterfire
flutterfire copied to clipboard
π [firebase_messaging] There is trying to call the onBackgroundMessage callback too early.
The problem is iOS specific. The problem occurs if the application was previously deleted from the memory. In this case, when the onBackgroundMessage callback is called in the didReceiveRemoteNotification method, it is still too early, because it was not yet possible to call the FirebaseMessaging.onBackgroundMessage method on the flutter side.
I solved the problem with a delay, but it's not a very nice solution. Can you fix the problem, or can you advise on a better solution?
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[_channel invokeMethod:@"Messaging#onBackgroundMessage"
arguments:notificationDict
result:^(id _Nullable result) {
@synchronized(self) {
if (completed == NO) {
completed = YES;
completionHandler(UIBackgroundFetchResultNewData);
if (backgroundTaskId != UIBackgroundTaskInvalid) {
[application endBackgroundTask:backgroundTaskId];
backgroundTaskId = UIBackgroundTaskInvalid;
}
}
}
}];
});
@sirkalmi
Can you provide flutter doctor -v
, a minimal code sample along with steps to replicate and logs that shows the behavior ?
Output from flutter doctor:
[β] Flutter (Channel stable, 3.3.2, on macOS 12.6 21G115 darwin-x64, locale hu-HU)
β’ Flutter version 3.3.2 on channel stable at /Users/sirkalmi/Programok/flutter
β’ Upstream repository https://github.com/flutter/flutter.git
β’ Framework revision e3c29ec00c (7 days ago), 2022-09-14 08:46:55 -0500
β’ Engine revision a4ff2c53d8
β’ Dart version 2.18.1
β’ DevTools version 2.15.0
[β] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
β’ Android SDK at /Users/sirkalmi/Programok/android-sdks/
β’ Platform android-33, build-tools 30.0.2
β’ Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
β’ Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
β’ All Android licenses accepted.
[β] Xcode - develop for iOS and macOS (Xcode 14.0)
β’ Xcode at /Applications/Xcode.app/Contents/Developer
β’ Build 14A309
β’ CocoaPods version 1.11.3
[β] Chrome - develop for the web
β’ Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[β] Android Studio (version 2021.2)
β’ Android Studio at /Applications/Android Studio.app/Contents
β’ Flutter plugin can be installed from:
π¨ https://plugins.jetbrains.com/plugin/9212-flutter
β’ Dart plugin can be installed from:
π¨ https://plugins.jetbrains.com/plugin/6351-dart
β’ Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
[β] IntelliJ IDEA Ultimate Edition (version 2022.1)
β’ IntelliJ at /Applications/IntelliJ IDEA.app
β’ Flutter plugin can be installed from:
π¨ https://plugins.jetbrains.com/plugin/9212-flutter
β’ Dart plugin can be installed from:
π¨ https://plugins.jetbrains.com/plugin/6351-dart
[β] IntelliJ IDEA Community Edition (version 2019.2.4)
β’ IntelliJ at /Applications/IntelliJ IDEA CE.app
β’ Flutter plugin can be installed from:
π¨ https://plugins.jetbrains.com/plugin/9212-flutter
β’ Dart plugin can be installed from:
π¨ https://plugins.jetbrains.com/plugin/6351-dart
[β] Connected device (4 available)
β’ sdk gphone x86 (mobile) β’ emulator-5554 β’ android-x86 β’ Android 11 (API 30) (emulator)
β’ Mobile iPhone-ja (mobile) β’ 1d331ec4db8d495d84aa0e6afd2790d8ecc1555f β’ ios β’ iOS 16.0 20A362
β’ macOS (desktop) β’ macos β’ darwin-x64 β’ macOS 12.6 21G115 darwin-x64
β’ Chrome (web) β’ chrome β’ web-javascript β’ Google Chrome 105.0.5195.125
[β] HTTP Host Availability
β’ All required HTTP hosts are available
β’ No issues found!
Logging output after deletion from memory when the first push arrives:
14:59:26.923310+0200 Runner push_debug_native application:didFinishLaunchingWithOptions
14:59:27.037193+0200 Runner push_debug_native didReceiveRemoteNotification:fetchCompletionHandler
14:59:27.080037+0200 Runner flutter: 14:59:27 DEBUG push_debug_flutter _initFirebaseServices
14:59:27.080969+0200 Runner flutter: 14:59:27 DEBUG push_debug_flutter initFirebaseMessaging
You can see that the didReceiveRemoteNotification method was run too early, before initFirebaseMessaging.
And when the second push arrives, when FirebaseMessaging has already been initialized:
15:38:29.089695+0200 Runner push_debug_native application:didFinishLaunchingWithOptions
15:38:29.241386+0200 Runner flutter: 15:38:29 DEBUG push_debug_flutter _initFirebaseServices
15:38:29.242434+0200 Runner flutter: 15:38:29 DEBUG push_debug_flutter initFirebaseMessaging
15:38:30.223824+0200 Runner push_debug_native didReceiveRemoteNotification:fetchCompletionHandler
15:38:30.226038+0200 Runner flutter: 15:38:30 DEBUG push_debug_flutter _onBackgroundMessage
Everything is fine here, this is the correct run order.
Code snippet from flutter side:
void main() async {
flavorConfig = FlavorConfig(...);
run();
}
Future<void> run() async {
WidgetsFlutterBinding.ensureInitialized();
await initPackageInfo();
await initStorageWrapper();
await _initFlavorConfig();
**await _initFirebaseServices();**
await NotificationsController.initializeLocalNotifications();
await _initSingletons();
await _initWorker();
await _initPreferredOrientations();
await _initFlutterDownloader();
_initGoogleMaps();
runApp(await getApp());
}
Future<void> _initFirebaseServices() async {
logger.d('push_debug _initFirebaseServices');
await Firebase.initializeApp(options: F.firebaseOptions);
...
await NotificationsController.initFirebaseMessaging();
}
static Future<void> initFirebaseMessaging() async {
logger.d('push_debug initFirebaseMessaging');
...
**FirebaseMessaging.onBackgroundMessage(_onBackgroundMessage);**
FirebaseMessaging.onMessage.listen(_onMessage);
...
}
static Future<void> _onBackgroundMessage(RemoteMessage message) async {
logger.d('push_debug _onBackgroundMessage');
await handleNotification(message);
}
Code snippet from iOS side:
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
NSLog("parking_push application:didFinishLaunchingWithOptions");
if let googleServiceFileName: String = Bundle.main.object(forInfoDictionaryKey: "GoogleService-Info") as! String? {
if let filePath = Bundle.main.path(forResource: googleServiceFileName, ofType: "plist") {
if let fileopts = FirebaseOptions(contentsOfFile: filePath) {
NSLog("Loading GoogleService-Info configuration...")
FirebaseApp.configure(options: fileopts)
}
}
}
if let googleApiKey: String = Bundle.main.object(forInfoDictionaryKey: "GOOGLE_API_KEY") as! String? {
GMSServices.provideAPIKey(googleApiKey)
}
GeneratedPluginRegistrant.register(with: self)
SwiftAwesomeNotificationsPlugin.setPluginRegistrantCallback { registry in
SwiftAwesomeNotificationsPlugin.register(
with: registry.registrar(forPlugin: "io.flutter.plugins.awesomenotifications.AwesomeNotificationsPlugin")!)
FLTSharedPreferencesPlugin.register(
with: registry.registrar(forPlugin: "io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin")!)
}
FlutterDownloaderPlugin.setPluginRegistrantCallback({registry in
if (!registry.hasPlugin("FlutterDownloaderPlugin")) {
FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
...
print("Received an APNs device token: \(readableToken)")
}
override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
super.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
print("PUSH registration failed: \(error)")
}
}
@sirkalmi Iam face same problem can you let me know where to put this code i want to try
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[_channel invokeMethod:@"Messaging#onBackgroundMessage"
arguments:notificationDict
result:^(id _Nullable result) {
@synchronized(self) {
if (completed == NO) {
completed = YES;
completionHandler(UIBackgroundFetchResultNewData);
if (backgroundTaskId != UIBackgroundTaskInvalid) {
[application endBackgroundTask:backgroundTaskId];
backgroundTaskId = UIBackgroundTaskInvalid;
}
}
}
}];
});
Or if you can fork the package and add it will be great
@sirkalmi thanks for the effort!
@hatemragab
-
Fork Flutterfire
-
Edit code here and push https://github.com/firebase/flutterfire/blob/master/packages/firebase_messaging/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m
-
Add your FirebaseMessaging fork to yalm: firebase_messaging: git: url: https://github.com/your_project/flutterfire.git ref: master path: packages/firebase_messaging/firebase_messaging
Unfortunately, I just tested it and it doesnt fix #9536. Code is a bit different but for us notifications work like before. @sirkalmi Does it fix 9536 for you? I mean, you kill the app (not just move it to background) and then open a different app. Do notifications work in that specific condition?
Unfortunately, I just tested it and it doesnt fix #9536. Code is a bit different but for us notifications work like before. @sirkalmi Does it fix 9536 for you? I mean, you kill the app (not just move it to background) and then open a different app. Do notifications work in that specific condition?
I didn't know about bug #9536, it doesn't fix it. :-( I have other things to do today, but I'll debug #9536 as soon as I can.
@hatemragab
Iam face same problem can you let me know where to put this code i want to try
- line: https://github.com/firebase/flutterfire/blob/master/packages/firebase_messaging/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m
Thanks for the updates. I am keeping this issue open for team's insight on expected behavior.
/cc @russellwheatley
@sirkalmi Hello did you have a chance to debug it?
@sirkalmi Hello did you have a chance to debug it?
No, because after clearing the application from memory, the connection to XCode's debugger is lost. Instead, I filled the code with logs, so it became visible where the processing gets stuck. I was clearly able to reproduce the bug and my fix works based on initial tester feedback.
Did testers kill app or just move it to background? We just tested again on another iphone: if app is killed and another app is opened, then notifications dont work.
Did testers kill app or just move it to background? We just tested again on another iphone: if app is killed and another app is opened, then notifications dont work.
They killed it but didn't open a new app. There seems to be a difference between just killing it or opening a new app afterwards.
Yes, if you kill app, notifications still work untill you open a different app. This is why bug can go unnoticed at first glance
Hello everyone, weβve reviewed the issues opened for iOS background messages and we want to make our position clear.
Android and iOS handle background messaging differently. The decision made by the iOS operating system whether messages reaches the relevant iOS event handler (and subsequently received by the Dart background messaging handler) is based on a number of criteria, such as: CPU usage, priority level (data-only messages are considered βlow priorityβ by iOS, android does not), battery level, amount of messages being received by the app, background/terminated application state, etc. This is a fundamental difference between the platforms, and you need to be aware of them when designing your application.
One solution suggested for iOS data-only messages unreliability has been to add a NotificationExtension. But this will still not create parity with Android as you will still have to include a notification with your message (therefore not data-only). It will still render the original notification if you do not update/mutate the notification that comes through the system in a timely manner. We are not currently considering this as a solution due to these limitations.
We also will not support non-FCM messages from third party packages. We specifically only support messages received from the Firebase APIs since we cannot guarantee that messages received from third party packages will not have any unintended side-effects on other Firebase products such as messaging delivery reporting and Analytics data.
To help us triage and locate genuine issues that need to be addressed we have created a specific issue template for iOS background messages. If you believe you still have an issue that needs to be addressed, please create a new issue following this template.
I will be closing this issue in favor of raising a new issue with the new template above. This template will help you provide us with all the information we need to investigate a potential issue with background messaging on iOS.