Cloud Function for Firebase sendToDevice not showing as notification on ios - ios

Upon sending a successful notification via Cloud Functions for Firebase, the notification is not shown as a push notification on ios device. I have found a couple of similar issues, but none present a clear solution.
cloud function:
exports.sendInitialNotification = functions.database.ref('branches/{branchId}/notifications/{notifId}').onWrite(event => {
const data = event.data.val()
if (data.finished) return
const tokens = []
const notifId = event.params.notifId
const getPayload = admin.database().ref(`notifications/${notifId}`).once('value').then(snapshot => {
const notif = snapshot.val()
const payload = {
notification: {
title: notif.title,
body: notif.message,
},
data: {
'title': notif.title,
'message': notif.message,
'id': String(notif.id),
}
}
if (notif.actions) {
payload.data['actions'] = JSON.stringify(notif.actions)
}
console.log('payload:', payload)
return payload
}, (error) => {
console.log('error at sendInitialNotification getPayload():', error)
})
const getTokens = admin.database().ref(`notifications/${notifId}/users`).once('value').then(snapshot => {
const users = snapshot.forEach((data) => {
let promise = admin.database().ref(`users/${data.key}/profile/deviceToken`).once('value').then(snap => {
if (tokens.indexOf(snap.val()) !== -1 || !snap.val()) return
return snap.val()
}, (error) => {
console.log('error retrieving tokens:', error)
})
tokens.push(promise)
})
return Promise.all(tokens)
}, (error) => {
console.log('error at sendInitialNotification getTokens()', error)
}).then((values) => {
console.log('tokens:', values)
return values
})
return Promise.all([getTokens, getPayload]).then(results => {
const tokens = results[0]
const payload = results[1]
if (payload.actions) {
payload.actions = JSON.stringify(payload.actions)
}
const options = {
priority: "high",
}
admin.messaging().sendToDevice(tokens, payload, options)
.then(response => {
data.finished = true
admin.database().ref(`notifications/${notifId}`).update({success: true, successCount: response.successCount})
console.log('successfully sent message', response)
}).catch(error => {
data.finished = true
admin.database().ref(`notifications/${notifId}`).update({success: false, error: error})
console.log('error sending message:', error)
})
})
})
...and the logs in firebase console:
successfully sent message { results: [ { error: [Object] } ],
canonicalRegistrationTokenCount: 0, failureCount: 1, successCount:
0, multicastId: 7961281827678412000 }
tokens: [
'eS_Gv0FrMC4:APA91bEBk7P1lz...' ]
payload: { notification: { title: 'test07', body: 'test07' }, data:
{ title: 'test07', message: 'test07', id: '1502383526361' } }
...but alas no notification shown on iphone. Im sure I am missing something along the OOO (order of operations) here, but not sure where the snag is. If anyone can point out my flaw please feel free to publicly chastise.
As always thank you in advance and any and all direction is appreciated!

The issue I was seeing was due to the reverse domain id in xcode not matching what was in firebase.
Typo

Related

Expo Notification Error in ios [Error: Another async call to this method is in progress. Await the first Promise.]

Please provide the following:
SDK Version: "^42.0.0",
Platforms(Android/iOS/web/all): iOS
Add the appropriate "Tag" based on what Expo library you have a question on.
Hello,
[expo-notifications] Error encountered while updating server registration with latest device push token., [Error: Another async call to this method is in progress. Await the first Promise.]
This error often occurs in expo app. And always occurs in build ios app.
Using the following code:
async function registerForPushNotificationsAsync() {
let token
if (Constants.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync()
let finalStatus = existingStatus
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync({
ios: {
allowAlert: true,
allowBadge: true,
allowSound: true,
allowAnnouncements: true,
},
})
finalStatus = status
}
if (finalStatus !== 'granted') {
console.log('noti cancel')
return
}
// token = (await Notifications.getExpoPushTokenAsync()).data
try {
await Notifications.getExpoPushTokenAsync().then((res) => {
console.log('getExpoPUshTokenAsync >> ', res)
token = res.data
})
} catch (err) {
console.log('getExpoPushTokenAsync error >> ', err)
}
} else {
Alert.alert(
'Must use physical device for Push Notifications'
)
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
})
}
return token
}
I have received a new credential, but I am still getting the error.

IOS Notification Permission alert does not show

SDK Version: 39.0.0
Platforms(Android/iOS/web/all): All
I am not getting accept or decline notifications permissions alert when loading my app in production.
I have tried clearing certificates and keys and allowing expo to add everything from a clean slate, but still no luck. I am starting to think maybe it’s my code which is the reason why the alert doesn’t get fired.
import Constants from "expo-constants";
import * as Notifications from "expo-notifications";
import { Permissions } from "expo-permissions";
import { Notifications as Notifications2 } from "expo";
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false
})
});
export default class LoginScreen extends React.Component {
state = {
email: "",
password: "",
notification: {},
errorMessage: null
};
async componentDidMount() {
this.registerForPushNotificationsAsync();
//Notifications.addNotificationReceivedListener(this._handleNotification);
Notifications2.addListener(data => {
this.setState({ notification: data });
});
Notifications.addNotificationResponseReceivedListener(
this._handleNotificationResponse
);
}
_handleNotification = notification => {
this.setState({ notification: notification });
};
_handleNotificationResponse = response => {
console.log(response);
};
handleLogin = async () => {
try {
const { email, password } = this.state;
const expoPushToken = await Notifications.getExpoPushTokenAsync();
console.log(expoPushToken);
const userinfo = await firebase
.auth()
.signInWithEmailAndPassword(email, password);
console.log("user ID ", userinfo.user.uid);
await firebase
.firestore()
.collection("users")
.doc(userinfo.user.uid.toString())
.update({
expo_token: expoPushToken["data"]
})
.then(function() {
console.log("token successfully updated!");
})
.catch(function(error) {
// The document probably doesn't exist.
console.error("Error updating document: ", error);
});
} catch (error) {
console.log("=======Error in login", error);
this.setState({ errorMessage: error.message });
}
};
registerForPushNotificationsAsync = async () => {
if (Constants.isDevice) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Permissions.askAsync(
Permissions.NOTIFICATIONS
);
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
const token = await Notifications.getExpoPushTokenAsync();
console.log(token);
//this.setState({ expoPushToken: token });
} else {
alert("Must use physical device for Push Notifications");
}
if (Platform.OS === "android") {
Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C"
});
}
};
import { Permissions } from "expo-permissions";
should of been
import * as Permissions from 'expo-permissions';
Sometimes we all make simple mistakes.

Video as a background image not working in Gatsby PWA on iOS

I created a opt-in app for potential interims for our company, i worked with Gatsby and for now am quite satisfied with the result. I made it an Progressive Web App as that is fairly easy with the gatsby plugin.
The PWA works great on Android and shows the background video as expected, but on iOS the video doesn't show.
I updated all the packages and dependencies to the last versions but that doesn't change a thing. I tried googling the issue but got a lot of search results off people trying to let a PWA play video in the background when the app is closed (not my case).
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Afstuderen bij Arcady`,
short_name: `Afstuderen`,
start_url: `/`,
background_color: `#FFF`,
theme_color: `#00a667`,
display: `standalone`,
icon: `src/images/bear_green.png`,
},
},
'gatsby-plugin-offline',
And the content of the service worker
importScripts("workbox-v3.6.3/workbox-sw.js");
workbox.setConfig({modulePathPrefix: "workbox-v3.6.3"});
workbox.core.setCacheNameDetails({prefix: "gatsby-plugin-offline"});
workbox.skipWaiting();
workbox.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
*/
self.__precacheManifest = [
{
"url": "webpack-runtime-aec2408fe3a97f1352af.js"
},
{
"url": "app-5b624d17337895ddf874.js"
},
{
"url": "component---node-modules-gatsby-plugin-offline-app-shell-js-b97c345e19bb442c644f.js"
},
{
"url": "offline-plugin-app-shell-fallback/index.html",
"revision": "ac0d57f6ce61fac4bfa64e7e08d076c2"
},
{
"url": "0-d2c3040ae352cda7b69f.js"
},
{
"url": "component---src-pages-404-js-cf647f7c3110eab2f912.js"
},
{
"url": "static/d/285/path---404-html-516-62a-0SUcWyAf8ecbYDsMhQkEfPzV8.json"
},
{
"url": "static/d/604/path---offline-plugin-app-shell-fallback-a-30-c5a-BawJvyh36KKFwbrWPg4a4aYuc8.json"
},
{
"url": "manifest.webmanifest",
"revision": "5a580d53785b72eace989a49ea1e24f7"
}
].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerRoute(/(\.js$|\.css$|static\/)/, workbox.strategies.cacheFirst(), 'GET');
workbox.routing.registerRoute(/^https?:.*\.(png|jpg|jpeg|webp|svg|gif|tiff|js|woff|woff2|json|css)$/, workbox.strategies.staleWhileRevalidate(), 'GET');
workbox.routing.registerRoute(/^https?:\/\/fonts\.googleapis\.com\/css/, workbox.strategies.staleWhileRevalidate(), 'GET');
/* global importScripts, workbox, idbKeyval */
importScripts(`idb-keyval-iife.min.js`)
const WHITELIST_KEY = `custom-navigation-whitelist`
const navigationRoute = new workbox.routing.NavigationRoute(({ event }) => {
const { pathname } = new URL(event.request.url)
return idbKeyval.get(WHITELIST_KEY).then((customWhitelist = []) => {
// Respond with the offline shell if we match the custom whitelist
if (customWhitelist.includes(pathname)) {
const offlineShell = `/offline-plugin-app-shell-fallback/index.html`
const cacheName = workbox.core.cacheNames.precache
return caches.match(offlineShell, { cacheName }).then(cachedResponse => {
if (cachedResponse) return cachedResponse
console.error(
`The offline shell (${offlineShell}) was not found ` +
`while attempting to serve a response for ${pathname}`
)
return fetch(offlineShell).then(response => {
if (response.ok) {
return caches.open(cacheName).then(cache =>
// Clone is needed because put() consumes the response body.
cache.put(offlineShell, response.clone()).then(() => response)
)
} else {
return fetch(event.request)
}
})
})
}
return fetch(event.request)
})
})
workbox.routing.registerRoute(navigationRoute)
let updatingWhitelist = null
function rawWhitelistPathnames(pathnames) {
if (updatingWhitelist !== null) {
// Prevent the whitelist from being updated twice at the same time
return updatingWhitelist.then(() => rawWhitelistPathnames(pathnames))
}
updatingWhitelist = idbKeyval
.get(WHITELIST_KEY)
.then((customWhitelist = []) => {
pathnames.forEach(pathname => {
if (!customWhitelist.includes(pathname)) customWhitelist.push(pathname)
})
return idbKeyval.set(WHITELIST_KEY, customWhitelist)
})
.then(() => {
updatingWhitelist = null
})
return updatingWhitelist
}
function rawResetWhitelist() {
if (updatingWhitelist !== null) {
return updatingWhitelist.then(() => rawResetWhitelist())
}
updatingWhitelist = idbKeyval.set(WHITELIST_KEY, []).then(() => {
updatingWhitelist = null
})
return updatingWhitelist
}
const messageApi = {
whitelistPathnames(event) {
let { pathnames } = event.data
pathnames = pathnames.map(({ pathname, includesPrefix }) => {
if (!includesPrefix) {
return `${pathname}`
} else {
return pathname
}
})
event.waitUntil(rawWhitelistPathnames(pathnames))
},
resetWhitelist(event) {
event.waitUntil(rawResetWhitelist())
},
}
self.addEventListener(`message`, event => {
const { gatsbyApi } = event.data
if (gatsbyApi) messageApi[gatsbyApi](event)
})
I expect the iOS PWA (safari) to show the video as it does on Android but instead it gives a grey screen.
I hope some one can help me out or point me in the right direction.
How big is your video ?
Last time I checked, iOS has a limit of 50MB for the cache of a PWA, so if your video is bigger than 50MB, that may be the reason it works only on Android (which doesn't have such restrictions).
I found this blog post that helped me fix this problem
To add Range request handling to gatsby-plugin-offline, I added a script called range-request-handler.js with the following:
// range-request-handler.js
// Define workbox globally
importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js');
// Bring in workbox libs
const { registerRoute } = require('workbox-routing');
const { CacheFirst } = require('workbox-strategies');
const { RangeRequestsPlugin } = require('workbox-range-requests'); // The fix
// Add Range Request support to fetching videos from cache
registerRoute(
/(\.webm$|\.mp4$)/,
new CacheFirst({
plugins: [
new RangeRequestsPlugin(),
],
})
);
Then in my gatsby-config.js I configured the plugin to append the above script:
// gatsby-config.js
module.exports = {
// ...
plugins: [
// ...plugins
{
resolve: 'gatsby-plugin-offline',
options: {
appendScript: require.resolve('./range-request-handler.js'),
},
},
// ...plugins
],
// ...
};
Videos now work in the Safari browser for me. If there is a better way to implement this, I am all ears. For now it works as intended

don't display notification in ios device when use react-native-fcm

i'm using from react-native-fcm for recieve pushNotification and do all config in this document(https://github.com/evollu/react-native-fcm)
in ios device only recieve notification and call notificationListener that checked by console.log but dont display notification message and alert even test FCM.presentLocalNotification for show local notification still dont show notification
async componentDidMount() {
if (Platform.OS === 'ios') {
try {
const result = await FCM.requestPermissions({ badge: false, sound: true, alert: true });
} catch (e) {
console.error(e);
}
FCM.getFCMToken().then(token => {
if (token !== undefined) {
// this.props.onChangeToken(token);
} else {
console.log('TOKEN (getFCMToken)', 'null');
}
});
// FCM.getAPNSToken().then(token => {
// this.props.onAPNToken(token);
// });
FCM.getInitialNotification().then(notif => {
console.log('INITIAL NOTIFICATION', notif);
});
this.notificationListener = FCM.on(FCMEvent.Notification, async (notif) => {
console.log(" >> notificationListener: ", notif)
if (notif.local_notification) return;
FCM.presentLocalNotification({
body: 'tdtydt',
priority: "high",
title: 'notif.fcm.title',
sound: "default",
show_in_foreground: true,
local_notification: true,
icon: "ic_launcher",
status: "400"
});
});
this.refreshTokenListener = FCM.on(FCMEvent.RefreshToken, token => {
console.log('TOKEN (refreshUnsubscribe)', token);
this.props.onChangeToken(token);
});
FCM.enableDirectChannel();
this.channelConnectionListener = FCM.on(FCMEvent.DirectChannelConnectionChanged, (data) => {
console.log(`direct channel connected${data}`);
});
setTimeout(() => {
FCM.isDirectChannelEstablished().then(d => console.log('isDirectChannelEstablished', d));
}, 500);
}

How Do I send VoIP push notifications to an iOS device using Amazon SNS in Node JS

I am trying to send VoIP push notifications directly to an iOS device from an App server using the NodeJS package called sns-mobile and Amazon SNS API.
However, When I try to send VoIP pushes using the below code, here is the error message that I get. Could someone please suggest me where I am going wrong, I have been spending nearly half a day to resolve this.
Invalid parameter: JSON must contain an entry for 'default' or
'APNS_VOIP
var iOSApp = new SNS({
platform: SNS.SUPPORTED_PLATFORMS.IOS,
region: 'us-west-2',
apiVersion: '2010-03-31',
accessKeyId: 'XXXXXXXXXXXXX',
secretAccessKey: 'XXXXXXXXXXXXX',
platformApplicationArn: 'arn:aws:sns:us-west-2:3303035XXXXX:app/APNS_VOIP/VoIPPushesApp'
});
iOSApp.addUser('deviceID',
JSON.stringify({
"APNS_VOIP": JSON.stringify({aps:{alert:"Hello and have a good day."}})
})
, function(err, endpointArn) {
if(err) {
console.log("The Error is :****: "+JSON.stringify(err, null, 4));
throw err;
}
// Send a simple String or data to the client
iOSApp.sendMessage(endpointArn, 'Hi There!', function(err, messageId) {
//iOSApp.sendMessage(endpointArn, messageTest, function(err, messageId) {
if(err) {
console.log("The Error in end message is :****: "+JSON.stringify(err, null, 4));
throw err;
}
console.log('Message sent, ID was: ' + messageId);
});
});
Here is the code that send a VoIP notifications to the receiver device using its device VoIP token. When a VoIP notification is received, the device calls a function called, didReceiveIncomingPushWithPayload
var AWS = require('aws-sdk');
// Amazon SNS module
AWS.config.update({
accessKeyId : 'XXXXXXXXXXXXXXXX',
secretAccessKey : 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
region : 'us-west-2'
});
var amazonSNS = new AWS.SNS();
// 1. Create Platform Endpoint
var createPlatformEndpoint = function(req, res, next){
var deviceVoipToken = req.deviceVoipToken; // Obtaining the device VoIP token from the request object
amazonSNS.createPlatformEndpoint({
// App in Sandboxmode (ie running on device directly from Xcode)
PlatformApplicationArn: "arn:aws:sns:us-west-2:xxxxxxxxxxxx:app/APNS_VOIP_SANDBOX/CurieVoip",
// App in Production mode (ie running on device after archiving and installed on device with a provisioning profile)
//PlatformApplicationArn: "arn:aws:sns:us-west-2:xxxxxxxxxxxx:app/APNS_VOIP/CurieVoip",
Token: deviceVoipToken
}, function(err, data) {
if (err) {
console.log(err.stack);
response.json({"status": "failure", "statusCode" : 402})
return;
}
var endpointArn = data.EndpointArn;
req.endpointArn = data.EndpointArn; // Passing the EndpointArn to the next function
next()
})
}
// 2. Publish notification
var publishVoipNotification = function(req, res, next){
var endpointArn = req.endpointArn;
var payload = {
default : 'Hello World, default payload',
APNS_VOIP : {
aps: {
alert: 'Hi there',
sound: 'default',
badge: 1
}
}
};
// first have to stringify the inner APNS object...
payload.APNS = JSON.stringify(payload.APNS);
// then have to stringify the entire message payload
payload = JSON.stringify(payload);
console.log('sending push');
amazonSNS.publish({
MessageStructure : 'json',
Message : payload,
TargetArn : endpointArn
}, function(err, data) {
if (err) {
console.log("Error stack: "+err.stack);
var message = "There has been an error in Publishing message via AmazonSNS with error: "+err.stack;
res.json({"status": "failure", "statusCode" : 402, "message" : message})
return;
}
next();
});
}
// 3. Deleting platform endpoint after sending a voipNotification; Ref: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html#deleteEndpoint-property
var deleteEndpointArn = function(req, res){
var endpointArn = req.endpointArn;
var roomName = req.roomName;
var params = {
EndpointArn: endpointArn /* required */
};
amazonSNS.deleteEndpoint(params, function(err, data) {
if (err){
var message = "Unable to deleteEndpointArn, with error: "+err.stack;
res.json({"status": "failure", "statusCode" : 400, "message":message})
}
else{
var message = "Deleted endpointArn successfully";
res.json({"status": "success", "statusCode" : 200, "message":message})
}
});
}
router.post('/sendVoipNotificationToReceiver', [createPlatformEndpoint, publishVoipNotification, deleteEndpointArn]);
Go to Amazon SNS
Click on Mobile: Push notifications
Click on Create platform Application,
User Apple Credentials "Used for development in sandbox" check this checkbox if you want to test voip notification on sandbox.
Push certificate type -- Voip Push certificate
Upload Certificate .P12 files.
Save Form and copy ARN
Create Lambda function. Code example below
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set region
AWS.config.update({region: 'us-east-1'});
var amazonSNS = new AWS.SNS();
const isset = (s) => {
return typeof s !== typeof undefined ? true : false;
};
exports.handler = (event, context, callback) => {
let data = event['body-json'];
if (isset(data)) {
let getAction = data['action'];
let userToken = data['token'];
let webPayload = data['payload'];
if (isset(getAction) && getAction == 'APNS_VOIP') {
amazonSNS.createPlatformEndpoint({
PlatformApplicationArn: 'YOUR APPLICATION ARN',
Token: userToken
}, function (err, data1) {
if (err) {
//handle error
console.log(err)
} else {
//Publish notification
var endpointArn = data1.EndpointArn;
console.log("endpointArn",endpointArn);
var payload = {
default: 'Silent voip push notification',
APNS_VOIP: {
//aps:webPayload
"aps" : {
"alert" : {
"title" : "Call Request"
},
"data":webPayload,
"content-available":1,
"category":"GENERAL"
}
}
};
payload.APNS_VOIP = JSON.stringify(payload.APNS_VOIP);
payload = JSON.stringify(payload);
amazonSNS.publish({
MessageStructure: 'json',
Message: payload,
MessageAttributes:{
"AWS.SNS.MOBILE.APNS.PRIORITY":{"DataType":"String","StringValue":"10"},
"AWS.SNS.MOBILE.APNS.PUSH_TYPE":{"DataType":"String","StringValue":"voip"}
},
TargetArn: endpointArn
}, function (err, data2) {
if (err) {
//handle error
console.log(err)
} else {
var params = {
EndpointArn: endpointArn /* required */
};
//Deleting platform endpoint after sending a voipNotification;
amazonSNS.deleteEndpoint(params, function (err, data3) {
if (err) {
//handle error
//console.log(err);
callback(null, {
"status": "error",
"data": [],
"message": []
});
} else {
//code success
console.log("delete",data3);
callback(null, {
"status": "Success",
"data": data3,
"message":"notification sent successfully"
});
}
});
}
});
}
});
}
else if (isset(getAction) && getAction == 'APNS_VOIP_SANDBOX') {
amazonSNS.createPlatformEndpoint({
PlatformApplicationArn: 'YOUR APPLICATION ARN',
Token: userToken
}, function (err, data1) {
if (err) {
//handle error
console.log(err)
} else {
//Publish notification
var endpointArn = data1.EndpointArn;
console.log("endpointArn",endpointArn);
var payload = {
default: 'Silent voip push notification',
APNS_VOIP_SANDBOX: {
"aps" : {
"alert" : {
"title" : "Call Request"
},
"data":webPayload,
"content-available":1,
"category":"GENERAL"
}
}
};
payload.APNS_VOIP_SANDBOX = JSON.stringify(payload.APNS_VOIP_SANDBOX);
payload = JSON.stringify(payload);
amazonSNS.publish({
MessageStructure: 'json',
Message: payload,
MessageAttributes:{
"AWS.SNS.MOBILE.APNS.PRIORITY":{"DataType":"String","StringValue":"10"},
"AWS.SNS.MOBILE.APNS.PUSH_TYPE":{"DataType":"String","StringValue":"voip"}
},
TargetArn: endpointArn
}, function (err, data2) {
if (err) {
//handle error
console.log(err)
} else {
var params = {
EndpointArn: endpointArn /* required */
};
//Deleting platform endpoint after sending a voipNotification; Ref: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html#deleteEndpoint-property
amazonSNS.deleteEndpoint(params, function (err, data3) {
if (err) {
//handle error
//console.log(err);
callback(null, {
"status": "error",
"data": [],
"message": []
});
} else {
//code success
console.log("delete",data3);
callback(null, {
"status": "Success",
"data": data3,
"message":"notification sent successfully"
});
}
});
}
});
}
});
}
else {
callback(null, {
"status": "error",
"data": [],
"message": "You have not posted valid data."
});
}
}
};
https://docs.aws.amazon.com/sns/latest/dg/sns-send-custom-platform-specific-payloads-mobile-devices.html
How Do I send VoIP push notifications to an iOS device using Amazon SNS in Node JS

Resources