Search code examples
androidfirebaseionic-frameworkfirebase-cloud-messagingcapacitor

Capacitor Push Notification not working in Android


I have set up the Ionic/Capacitor app to receive push notifications following the capacitor tutorial,

I went through all the tutorial successfully and I receive test notifications sent from FCM both in foreground and background

I can successfully register to a topic I can receive notifications sent to the topic I can receive notifications sent to the token (test mode)

Now I'm trying to send notifications from a different device, I just created a two test button to send notification in multicast to an array of tokens and another button to send notification to a given topic

In both cases from my device I receive the notifications in foreground, but not in background

I believe is something wrong with the format I'm using to send the notifications that is not correct in case of background (I can receive those from the FCM test tool)

Client

const send = functions.httpsCallable('sendPushNotificationToUsers');
const sendToTopic = functions.httpsCallable('sendPushNotificationToTopic');
const sendNotification = useCallback(
  ({
    to,
    title,
    body,
    onClick
  }) => {
    setLoading(true);
    send({
        to,
        title,
        body,
        onClick
      })
      .then(() => setSuccess(true))
      .catch(() => setError(true))
      .finally(() => setLoading(false));
  }, [send],
);
const sendNotificationToTopic = useCallback(
  ({
    topic,
    title,
    body,
    onClick
  }) => {
    setLoading(true);
    sendToTopic({
        topic,
        title,
        body,
        onClick
      })
      .then(() => setSuccess(true))
      .catch(() => setError(true))
      .finally(() => setLoading(false));
  }, [sendToTopic],
);

Server / Functions

exports.sendPushNotificationToUsers = functions.https.onCall(
  (data, context) => {
    if (!context.auth) {
      throw new functions.https.HttpsError(
        'failed-precondition',
        'The function must be called ' + 'while authenticated.',
      );
    }
    db.collection('users_meta')
      .doc(data.to)
      .collection('messagingTokens')
      .get()
      .then(messagingTokens => {
        if (messagingTokens && messagingTokens.size) {
          const to = messagingTokens.docs.map(i => i.data().token);
          console.log(to); // I get to this console log and the tokens are printed correctly in an array
          admin.messaging().sendMulticast({
            title: data.title,
            body: data.body,
            data: {
              title: data.title,
              body: data.body,
              onClick: data.onClick || '',
            },
            tokens: to,
          });
        }
      });
  },
);

exports.sendPushNotificationToTopic = functions.https.onCall(
  (data, context) => {
    if (!context.auth) {
      throw new functions.https.HttpsError(
        'failed-precondition',
        'The function must be called ' + 'while authenticated.',
      );
    }
    admin.messaging().send({
      data: {
        title: data.title,
        body: data.body,
        onClick: data.onClick || '',
      },
      topic: data.topic,
    });
  },
);

Notifications handler

const initNative = useCallback(() => {
  PushNotifications.register();
  PushNotifications.requestPermission().then(result => {
    if (result.granted) {
      // Register with Apple / Google to receive push via APNS/FCM
      PushNotifications.register();
    } else {
      // Show some error
    }
  });
  PushNotifications.addListener(
    'registration',
    (token: PushNotificationToken) => {
      const device = new DeviceUUID().get();
      registerTokenToUser(device, token.value);
      alert('Push registration success, token: ' + token.value);
    },
  );
  PushNotifications.addListener('registrationError', (error: any) => {
    alert('Error on registration: ' + JSON.stringify(error));
  });
  PushNotifications.addListener(
    'pushNotificationReceived',
    (notification: PushNotification) => {
      alert(JSON.stringify(notification));
      // this array fires correctly with the app in foreground, but nothing on the notifications tray with the app in background if sent from my send functions, works correctly if sent from FCM
    },
  );
  // Method called when tapping on a notification
  PushNotifications.addListener(
    'pushNotificationActionPerformed',
    (notification: PushNotificationActionPerformed) => {
      alert(JSON.stringify(notification));
    },
  );
}, [PushNotifications, history, registerTokenToUser]);

Any suggestion? Thanks


Solution

  • I've found the error myself, for the notifications be visible in background mode the notification object needs to have "notification" key populated, that was missing in my case,

    correct send function should be

    exports.sendPushNotificationToUsers = functions.https.onCall(
      (data, context) => {
        if (!context.auth) {
          throw new functions.https.HttpsError(
            'failed-precondition',
            'The function must be called ' + 'while authenticated.',
          );
        }
        db.collection('users_meta')
          .doc(data.to)
          .collection('messagingTokens')
          .get()
          .then(messagingTokens => {
            if (messagingTokens && messagingTokens.size) {
              const to = messagingTokens.docs.map(i => i.data().token);
              console.log(to); // I get to this console log and the tokens are printed correctly in an array
              admin.messaging().sendMulticast({
                title: data.title,
                body: data.body,
                notification: {
                  title: data.title,
                  body: data.body,
                },
                data: {
                  title: data.title,
                  body: data.body,
                  onClick: data.onClick || '',
                },
                tokens: to,
              });
            }
          });
      },
    );
    
    exports.sendPushNotificationToTopic = functions.https.onCall(
      (data, context) => {
        if (!context.auth) {
          throw new functions.https.HttpsError(
            'failed-precondition',
            'The function must be called ' + 'while authenticated.',
          );
        }
        admin.messaging().send({
          notification: {
            title: data.title,
            body: data.body,
          },
          data: {
            title: data.title,
            body: data.body,
            onClick: data.onClick || '',
          },
          topic: data.topic,
        });
      },
    );