3

edit: it is supposed to look like it does on the device log, according to Firebase support

I am adding push notifications via FCM to my Flutter app, but the message format is very different on the iOS Simulator vs. my iPhone 5s. When receiving a push notification from the Firebase console to an active/opened app.

Problem: What do I need to do to make sure the real device receives the message in the correct format?


Log from Simulator (iPhone XR, 12.2) (looks like in the official code examples):

    onMessage: {
    from: 123000000000,
    collapse_key: com.mydomainnamehere,
    notification: {
        body: Lorem ipsum,
        title: Title,
        e: 1,
        tag: campaign_collapse_key_9876543210011223344
    }
}


Log from real device (iPhone 5s, 12.2) (can't find any references online to this):

onMessage: {
    google.c.a.c_l: notif_name,
    google.c.a.e: 1,
    aps: {
        alert: {
            title: Title,
            body: Lorem ipsum
        }
    },
    gcm.n.e: 1,
    google.c.a.c_id: 9876543210011223344,
    google.c.a.udt: 0,
    gcm.message_id: 1234567800998877,
    google.c.a.ts: 1234567800
}



The notification is sent from the Firebase console to all devices, the logs are taken from the same notification (but I anonymized the id's).

The Device and Simulator is running the same Flutter code from Android Studio, at the same time.

Parts of my pubspec.yaml that refers to FCM

  firebase_core: ^0.4.0+1
  firebase_auth: 0.11.1
  cloud_firestore: ^0.11.0+2
  firestore_ui: ^1.4.0
  firebase_messaging: ^5.0.2

Software and SDK Versions

Flutter Channel dev, v1.8.4, Mac OS X 10.14.5, Android SDK version 28.0.3, Xcode 10.2.1, Android Studio version 3.4

Flutter message-handling code

void initState() {
    super.initState();

    if (Platform.isIOS) {
      iosSubscription = _fcm.onIosSettingsRegistered.listen((IosNotificationSettings settings) {
        print("FCM settings received: $settings");
      });

      _fcm.requestNotificationPermissions(IosNotificationSettings());
    }

    _fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
      },
    );

    getFcmToken();
  }

  void getFcmToken() async {
    var token = await FirebaseMessaging().getToken();
    print("Token $token");
  }

I was expecting that the JSON format would be the same on both the simulator and a real device. But the real device isn't even receiving all of the notification.

Jonas357
  • 213
  • 2
  • 10
  • The only thing I see as issue is your plugin versions. Update them to the latest version because they are updated frequently to fix bugs. so it may be a bug from the plugin. – user2682025 Aug 14 '19 at 13:55
  • 1
    Thanks. I updated them now, but nothing changed. – Jonas357 Aug 14 '19 at 14:50
  • It seems that for the simulator, targeted messages do not arrive. But messages without a target token, sent from the console, does! – Jonas357 Aug 20 '19 at 09:40

2 Answers2

1

According to Firebase support, we should not be able to get push notifications in the simulator, and they say that the gcm-format above is indeed the correct one.

The solution is to always use key-value pairs as stated in the answer to this previous question FCM - Get Message Label

Jonas357
  • 213
  • 2
  • 10
  • There's a difference between push notifications and data notifications you can also send both at the same time or 1. The simulator accepts data notifications just not push. The device accepts both on iOS. – Oliver Dixon Feb 09 '20 at 07:32
1

For those who still struggle with this, there seems an alternative solution. While the structure of the data can be different in various conditions (iOS/Android or Real device/Simulator), the key names of those essential parts of the data are considered to be unique no matter how deeply nested: "title" and "body". Therefore, extracting the values of those 'title' and 'body' entries might solve the problem.

//get title value from the message load (whether data or push)
String _title = _findFirstKeyValue(message, 'title');

The following is a recursive function to get the first matching entry from the message Map.

  String _findFirstKeyValue(Map data, String targetKey) {
    for (final k in data.keys) {
      final v = data[k];

      if (v is Map) { // go deeper if the value is still a kind of Map
        final String _temp = _findFirstKeyValue(v, targetKey);
        if (_temp != '') return _temp;

      } else { // when the value is primitive Key-Value pair
        if (k.toString().toLowerCase() == targetKey.toLowerCase())  {
          return v.toString();
        }
      }
    }
    return '';
  }

Note that return will not be fired if you use data.forEach rather than ordinary for loop.

klados
  • 706
  • 11
  • 33