ExpoPushTicket does not contain the corresponding PushToken in case of error, how to know which pushToken is causing error to remove it from database

Viewed 196

I am trying to send push notifications to more than one users of my app (pretty common to send more than one users. e.g notifying all users of a new feature).

When sending notifications, ExpoPushTicket array contains errors e.g. DeviceNotRegistered and I want to remove these errored pushTokens from my database as it indicates that the user have uninstalled the app.

But the problem is that I am not able to distinguish between working and non working tokens as in the request, there are more than 1 tokens and in response there are more than 1 ExpoPushTicket Objects.

How I can correctly map and know which push tokens are producing the DeviceNotRegistered Error?

Here is a sample code and a sample response.

import { Expo, ExpoPushMessage, ExpoPushTicket } from "expo-server-sdk";

const sendNotifications = async (): Promise<ExpoPushTicket[]> => {
  const expo = new Expo();
  // 100 push tokens for example.
  const notifications: ExpoPushMessage[] = [
    {
      to: '<EXPO_PUSHTOKEN1>',
      title: 'New Feature',
      body: 'We have a fantastic new feature, you might be interested to checkout.',
    },
    {
      to: '<EXPO_PUSHTOKEN2>',
      title: 'New Feature',
      body: 'We have a fantastic new feature, you might be interested to checkout.',
    }
  ];
  const filteredNotifications = notifications.filter((noti) => Expo.isExpoPushToken(noti.to));
  
  const chunks = expo.chunkPushNotifications(filteredNotifications);
  const promises = [];
  chunks.forEach((chunk) => {
    promises.push(expo.sendPushNotificationsAsync(chunk));
  });
  
  const chunkTickets = await Promise.all(promises);
  console.log("chunk tickets", chunkTickets);
  
  // Chunk Tickets does not contain the corresponding pushToken in case of success/error,
  // so I am unable to know in case of error, which pushToken should be removed from database.
  // I tried to get the receipts
  
  const ticketIds = [];
  chunkTickets?.forEach((chunk) => {
    chunk?.forEach((ticket) => {
      if ( ticket.id ) {
        ticketIds.push(ticket.id);
      }
    });
  });
  
  const receipts = await expo.getPushNotificationReceiptsAsync(ticketIds);
  console.log("receipts", receipts);
  
  // But unfortunately, receipts are also not having the "pushToken" field
  // So I don't know which pushToken is causing error and should be removed from database
  
  return receipts;
};

Sample Response:

// Sample Response
const chunkTickets = [
  [
    {
      "id": "d4574aeb-6e68-474a-9f60-7cba340a7797",
      "status": "error",
      "message": "The recipient device is not registered with FCM.",
      "details": {
        "error": "DeviceNotRegistered",
        "fault": "developer"
      }
    }
  ]
]

const receipts = {
  "d4574aeb-6e68-474a-9f60-7cba340a7797": {
    "status": "error",
    "message": "The recipient device is not registered with FCM.",
    "details": {
      "fault": "developer",
      "error": "DeviceNotRegistered",
      "sentAt": 1645233143
    },
    "__debug": {}
  }
}
1 Answers

Your chunks in const chunks = expo.chunkPushNotifications(filteredNotifications); have the push token in them and they directly correspond to your chunkTickets and your receipts.

I was able to do something like this

return Promise.all(
    chunks.map((chunk) => expo.sendPushNotificationsAsync(chunk).then((ticketChunkResponse) => {
        tickets.push(_.merge(...ticketChunkResponse, ...chunk));
        return Promise.resolve(true);
      }),
    ),
  ).then((res) => {
     checkReceipts(tickets);
  });

where I merged the chunks with their corresponding responses before sending them to my checkReceipt function

Related