I'm trying to write a cloud function which sends a push notification to an iOS device. The logs say that sendToDevice was successful. But my device isn't receiving any notifications. Neither Xcode nor Cloud Functions are showing any errors. How can I diagnose this problem?
My cloud function takes a registration token from the realtime database. This token is saved to the database during the didRegisterForRemoteNotificationsWithDeviceToken function in the ios app, confirming that the front end is registering for remote notifications. The app has been given permission to show notifications and the push notification capabilities have been enabled in Xcode.
This block of code comes from my cloud function (Node.js):
// This snapshot was taken from the realtime database
// Xcode logs confirmed that this function is receiving the correct key
const notificationKey = userSnapshot.child("notificationKey").val();
const payload = {
notification: {
title: 'Test Notification Title',
body: 'Test Notification Body',
sound: 'default',
badge: '1'
}
};
return admin.messaging().sendToDevice(notificationKey, payload).then(function (response) {
console.log("Successfully sent message: ", JSON.stringify(response));
return;
}).catch(function (error) {
console.log("Error sending message: ", error);
return;
});
When calling the cloud function above, the logs showed this console log (Id numbers truncated):
"Successfully sent message: {"results":[{"messageId":"0:154/* ... */"}],"canonicalRegistrationTokenCount":0,"failureCount":0,"successCount":1,"multicastId":576/* ... */}"
But my test device (iPhone 7) hasn't received any notifications. My app has the following delegate functions (Swift 4):
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Notification will present: \(notification.request.content.userInfo)")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Notification received: \(response.notification.request.content.userInfo)")
}
Neither print statement is appearing in Xcode's output. The only relevant print statements found are the ones I included in didRegisterForRemoteNotificationsWithDeviceToken. My APNs certificate is apparently still valid and has not expired.
The issue was simply that my podfile was missing:
pod 'Firebase/Messaging'
That allowed my didReceiveRemoteNotification to receive the notification payloads from Firebase cloud function. Then once I added UNUserNotificationCenter.current().delegate = self to the AppDelegate, the UNUserNotificationCenterDelegate functions worked as intended.
Strange how the missing pod didn't give me any compiler errors.
Related
I'm trying to send push notifications using Firebase cloud functions.
I'm using https://github.com/expo/expo-server-sdk-node
When I try to deploy I'm getting some errors. (only getting the errors with earlier code and package required, and not when just deploying my hello world function)
the code
const functions = require("firebase-functions");
const { Expo } = require("expo-server-sdk");
// Create and Deploy Your First Cloud Functions
// https://firebase.google.com/docs/functions/write-firebase-functions
exports.helloWorld = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", { structuredData: true });
response.send("Hello from Firebase");
});
exports.notifications = functions.firestore
.document("notifications/{id}")
.onCreate((snap, context) => {
// console.log("-----snap", snap.data());
// console.log("-----context", context);
// console.log("-----context.params", context.params);
// Create a new Expo SDK client
// optionally providing an access token if you have enabled push security
let expo = new Expo({ accessToken: process.env.EXPO_ACCESS_TOKEN });
// Create the messages that you want to send to clients
let messages = [];
for (let pushToken of somePushTokens) {
// Each push token looks like ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]
// Check that all your push tokens appear to be valid Expo push tokens
if (!Expo.isExpoPushToken(pushToken)) {
console.error(`Push token ${pushToken} is not a valid Expo push token`);
continue;
}
// Construct a message (see https://docs.expo.io/push-notifications/sending-notifications/)
messages.push({
to: pushToken,
sound: "default",
body: "This is a test notification",
data: { withSome: "data" }
});
}
// The Expo push notification service accepts batches of notifications so
// that you don't need to send 1000 requests to send 1000 notifications. We
// recommend you batch your notifications to reduce the number of requests
// and to compress them (notifications with similar content will get
// compressed).
let chunks = expo.chunkPushNotifications(messages);
let tickets = [];
(async function test() {
// Send the chunks to the Expo push notification service. There are
// different strategies you could use. A simple one is to send one chunk at a
// time, which nicely spreads the load out over time:
for (let chunk of chunks) {
try {
let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
console.log(ticketChunk);
tickets.push(...ticketChunk);
// NOTE: If a ticket contains an error code in ticket.details.error, you
// must handle it appropriately. The error codes are listed in the Expo
// documentation:
// https://docs.expo.io/push-notifications/sending-notifications/#individual-errors
} catch (error) {
console.error(error);
}
}
})();
// Later, after the Expo push notification service has delivered the
// notifications to Apple or Google (usually quickly, but allow the the service
// up to 30 minutes when under load), a "receipt" for each notification is
// created. The receipts will be available for at least a day; stale receipts
// are deleted.
//
// The ID of each receipt is sent back in the response "ticket" for each
// notification. In summary, sending a notification produces a ticket, which
// contains a receipt ID you later use to get the receipt.
//
// The receipts may contain error codes to which you must respond. In
// particular, Apple or Google may block apps that continue to send
// notifications to devices that have blocked notifications or have uninstalled
// your app. Expo does not control this policy and sends back the feedback from
// Apple and Google so you can handle it appropriately.
let receiptIds = [];
for (let ticket of tickets) {
// NOTE: Not all tickets have IDs; for example, tickets for notifications
// that could not be enqueued will have error information and no receipt ID.
if (ticket.id) {
receiptIds.push(ticket.id);
}
}
let receiptIdChunks = expo.chunkPushNotificationReceiptIds(receiptIds);
(async function testTwo() {
// Like sending notifications, there are different strategies you could use
// to retrieve batches of receipts from the Expo service.
for (let chunk of receiptIdChunks) {
try {
let receipts = await expo.getPushNotificationReceiptsAsync(chunk);
console.log(receipts);
// The receipts specify whether Apple or Google successfully received the
// notification and information about an error, if one occurred.
for (let receiptId in receipts) {
let { status, message, details } = receipts[receiptId];
if (status === "ok") {
continue;
} else if (status === "error") {
console.error(
`There was an error sending a notification: ${message}`
);
if (details && details.error) {
// The error codes are listed in the Expo documentation:
// https://docs.expo.io/push-notifications/sending-notifications/#individual-errors
// You must handle the errors appropriately.
console.error(`The error code is ${details.error}`);
}
}
}
} catch (error) {
console.error(error);
}
}
})();
});
the errors
$ firebase deploy --only functions
=== Deploying to 'appName'...
i deploying functions
i functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i functions: ensuring required API cloudbuild.googleapis.com is enabled...
i functions: ensuring required API artifactregistry.googleapis.com is enabled...
+ functions: required API cloudbuild.googleapis.com is enabled
+ functions: required API cloudfunctions.googleapis.com is enabled
+ functions: required API artifactregistry.googleapis.com is enabled
i functions: preparing functions directory for uploading...
i functions: packaged functions (72.9 KB) for uploading
+ functions: functions folder uploaded successfully
i functions: updating Node.js 16 function helloWorld(us-central1)...
i functions: updating Node.js 16 function notifications(us-central1)...
Functions deploy had errors with the following functions:
helloWorld(us-central1)
notifications(us-central1)
i functions: cleaning up build files...
Error: There was an error deploying functions:
- Error Failed to update function helloWorld in region us-central1
- Error Failed to update function notifications in region us-central1
$ firebase deploy --only functions
=== Deploying to 'appName'...
i deploying functions
i functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i functions: ensuring required API cloudbuild.googleapis.com is enabled...
i functions: ensuring required API artifactregistry.googleapis.com is enabled...
+ functions: required API cloudfunctions.googleapis.com is enabled
+ functions: required API artifactregistry.googleapis.com is enabled
+ functions: required API cloudbuild.googleapis.com is enabled
i functions: preparing functions directory for uploading...
i functions: packaged functions (72.9 KB) for uploading
+ functions: functions folder uploaded successfully
i functions: updating Node.js 16 function helloWorld(us-central1)...
i functions: updating Node.js 16 function notifications(us-central1)...
Functions deploy had errors with the following functions:
helloWorld(us-central1)
notifications(us-central1)
i functions: cleaning up build files...
Error: There was an error deploying functions:
- Error Failed to update function helloWorld in region us-central1
- Error Failed to update function notifications in region us-central1
I'm not sure what to even try at this point. did some searching and it doesn't seem to be that common of an issue and haven't found any solutions at this point.
Let me know if I should be supplying more info.
I can run $ firebase functions:log or ...-debug and share the output.
Any help would be greatly appreciated.
Thanks!
The package was installed in the node modules folder of the project and not the node-modules for the functions folder.
We are using ActiveMQ 5.16.1 with the stompit client to create a durable subscription in our NodeJS app using the following code snippet:
var connectOptions = {
"host": "",
"port": amqPort,
rejectUnauthorized: false,
checkServerIdentity: () => { },
"connectHeaders": {
"heart-beat": "15000,15000",// hear-beat of 15 seconds
'login': 'admin',
'passcode': 'admin',
'client-id' : "agent-manager"
}
};
var server1 = connectOptions;
server1.host = amqPrimaryHost;
var server2 = connectOptions;
server2.host = amqSecondaryHost;
var amqSubscription;
var subscribeHeaders = {
"destination": "/topic/delivery-reports",
"activemq.subscriptionName": "channel_manager_delivery_reports",
"ack": "client-individual"
};
var connectionManager = new stompit.ConnectFailover([server1,server2], reconnectOptions);
connectionManager.connect(function (error, client, reconnect){
if (error) {
logger.error("Terminal error, gave up reconnecting ", error);
return;
}
client.on("error", function (error) {
if(!client)
reconnect();
});
amqSubscription=client.subscribe(subscribeHeaders, function (error, message,subscription) {
logger.info("going to subscribe")
if (error) {
logger.error("Subscription failed. Going to disconnect", error);
subscription.unsubscribe();
// reconnect();
}
logger.info("subscribed")
});
});
function unsubscribe () {
logger.info("Going to unsub")
amqSubscription.unsubscribe({"activemq.subscriptionName":"channel_manager_delivery_reports"})
};
However, when I call the unsubscribe, it only changes the Subscriber active status to false but does not remove it from the active subscribers' list as shown in the screenshot.
Getting the following exception in stomp.logs.
2021-05-12 05:20:14,826 [0.1:50251#61613] WARN ProtocolConverter - Exception occurred for client agent-manager (tcp://127.0.0.1:50251) processing: UNSUBSCRIBE -> javax.jms.JMSException: Durable consumer is in use
2021-05-12 05:20:14,826 [0.1:50251#61613] DEBUG ProtocolConverter - Exception detail
javax.jms.JMSException: Durable consumer is in use
at org.apache.activemq.broker.region.TopicRegion.removeSubscription(TopicRegion.java:220)
at org.apache.activemq.broker.region.RegionBroker.removeSubscription(RegionBroker.java:457)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.advisory.AdvisoryBroker.removeSubscription(AdvisoryBroker.java:396)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.broker.TransportConnection.processRemoveSubscription(TransportConnection.java:419)
at org.apache.activemq.command.RemoveSubscriptionInfo.visit(RemoveSubscriptionInfo.java:81)
at org.apache.activemq.broker.TransportConnection.service(TransportConnection.java:331)
at org.apache.activemq.broker.TransportConnection$1.onCommand(TransportConnection.java:200)
at org.apache.activemq.transport.MutexTransport.onCommand(MutexTransport.java:45)
at org.apache.activemq.transport.AbstractInactivityMonitor.onCommand(AbstractInactivityMonitor.java:301)
at org.apache.activemq.transport.stomp.StompTransportFilter.sendToActiveMQ(StompTransportFilter.java:97)
at org.apache.activemq.transport.stomp.ProtocolConverter.sendToActiveMQ(ProtocolConverter.java:179)
at org.apache.activemq.transport.stomp.ProtocolConverter.onStompUnsubscribe(ProtocolConverter.java:714)
at org.apache.activemq.transport.stomp.ProtocolConverter.onStompCommand(ProtocolConverter.java:251)
at org.apache.activemq.transport.stomp.StompTransportFilter.onCommand(StompTransportFilter.java:85)
at org.apache.activemq.transport.TransportSupport.doConsume(TransportSupport.java:83)
at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:233)
at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:215)
at java.lang.Thread.run(Thread.java:748)
2021-05-12 05:20:14,827 [0.1:50251#61613] TRACE ProtocolConverter - Command that caused the error: UNSUBSCRIBE
activemq.subscriptionName:channel_manager_delivery_reports
receipt:1
id:1
Any suggestion on how to remove the durable subscription properly via stompit.
You first need to disconnect the connection which the durable subscriber is using. This deactivates the subscription and will prevent the JMSException: Durable consumer is in use you're seeing.
Then you need to reconnect using the same client-id header value which you used on your CONNECT frame for the connection used to subscribe.
Then need to pass the activemq.subscriptionName header in the UNSUBSCRIBE frame just like you did for the SUBSCRIBE frame, e.g.:
amqSubscription.unsubscribe({"activemq.subscriptionName": "channel_manager_delivery_reports"})
To be clear, this was resolved awhile back via AMQ-1890. You can see the corresponding source code which checks for the header and removes the subscription. You can also see the unit test which subscribes, disconnects, reconnects, and unsubscribes the durable subscription.
If you still have trouble then it may be worth turning on STOMP trace logging in the broker using these instructions to ensure the UNSUBSCRIBE frame is being received with the expected activemq.subscriptionName header.
So here's the thing - I have a node.js backend server for my Android App. I am using the Google Play billing library, and using the backend to verify the purchase as google Docs recommend.
Now, all the other answers out there regarding this error seem to refer to a consistent problem.
My backend SOMETIMES verifies, and SOMETIMES comes back with this as an error, indicating that in fact, my service account IS linked (as shows up in my consoles).
I tried two different 3rd party libraries, and I have the same issue. Sometimes one will respond with verification success, while the other will say my account is not linked. Sometimes they are both negative, sometimes both positive.
It seems inconsistent.
var platform = 'google';
var payment = {
receipt: purchaseToken, // always required ... this is google play purchaseToken
productId: subID, // my subscription sku id
packageName: 'com.xxxxxx', // my package name
keyObject: key, // my JSON file
subscription: true, // optional, if google play subscription
};
var promise2 = iap.verifyPayment(platform, payment, function (error, response) {
/* your code */
if (error) {
console.log('error with iap, ' , error);
return true;
} else {
console.log('success with iap, response is: ', response);
return true;
}
});
I also tried with a different library, got same results:
var receipt = {
packageName: "com.xxxx",
productId: subID, // sku subscription id
purchaseToken: purchaseToken // my purchase token
};
var promise = verifier.verifySub(receipt, function cb(err, response) {
if (err) {
console.log('within err, was there a response? : ', response);
console.log('there was an error validating the subscription: ', err);
//console.log(err);
return true;
} else {
console.log('sucessfully validated the subscription');
// More Subscription info available in “response”
console.log('response is: ', response );
return true;
}
});
// return promises later.
Any else experience this issue?
TLDR; Create a new product ID.
I eventually found the answer. The problem was not with my code, or with permissions in the Google Developer Console OR the Google Play Console. Everything was set up correctly except for one thing.
Previously, before setting up Test License Accounts in Google Play Console, I had made an actual Subscription purchase with real money on my productID "X".
Then, after adding the same google account that bought the subscription as a test user, I continued to test results on the same subscription, productID "X".
Even though I had cancelled the REAL purchase, the actual expiration date was not for another month.
Therefore, I believe sometimes Google was getting confused when I would buy/cancel the purchase - confusing the test subscription with the real subscription.
Creating a new Product ID, and only using that, solved my problem, and purchases are verified consistently.
I am using Amazon SNS Mobile Push Notifications both for android and ios. I am quite successful with sending push notification with text and icon only. Now i am trying to send the notification with an image bottom. I searched every where but couldn't find a perfect docs to work on. Any suggestions please.
i installed this package using npm , i used this to send push notification. please refer this link.
https://www.npmjs.com/package/sns-mobile
AWS_SNS_App.getUsers(function (err, allDevices) {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
if (allDevices.length != 0) {
var totalDevices = 0;
for (var i = 0; i < allDevices.length; i++) {
totalDevices = totalDevices + 1;
AWS_SNS_App.sendMessage(allDevices[i].EndpointArn, message, function (err, messageId) {
if (err) {
console.log('An error occured sending message to device %s');
res.send(err);
} else {
//res.send('Successfully sent a message to device , MessageID was : ' + messageId);
}
});
}
if (totalDevices === allDevices.length) {
res.send('Successfully sent a message to all devices');
}
}
}
});
sendMessage(endpointArn, message, callback) Send a message to a user.
The message parameter can be a String, or an Object with the formats
below. The callback format is callback(err, messageId).
from docs it indicates to send a endpointArn,message and we will get a callback of any response. what i suppose to send an image along with the image, is that possible or any another way to do that.
thanks.
Every image-containing push notification sent could contain a mediaReference that the app can later use to obtain content from a web service or from the apps bundled resources.
In any media case, the final resource link / bundle-resource-ref. can be composed within the app, (example) depending on other parameters within the push.
Remember that if the resource is not bundled you will have to download the image before displaying the notification (using it)
So the solution is in the client-side...
Implement specific methods for each of your platforms (android & ios), perform the required operations (i repeat, different and specific to the platform) in order to display the push notification with the image.
NOTE :
Tell me if you need references for building platform specific notifications with images. (and if so, what min sdk version you are using for each)
In Azure Mobile Service javascript backend ,trying to send a notification by calling notification hub through custom api. Its giving me a notification , but everytime giving a time out error too. I observed my logs and its showing "An error occurred creating push for user scripts: azure.notificationHubService could not be created" .
Instead of using azure.createNotificationHubService method , is there any way to import already created notification hub service ?How to get rid of the time out error. I am pasting function below.
function sendnotification() {
var azure = require('azure');
var notificationHubService = azure.createNotificationHubService('disapphub','Endpoint=sb://xxx.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=my_key_here=;');
var payload = {
data: {
"type":"newthread",
}
};
var mytags ='admin';
notificationHubService.gcm.send(mytags, payload, function(error){
if(!error){
//notification sent
}
});
}