I'm writing an app that uses push notifications widely, so push notifications reliability is very important for app.
I send push notification through parse cloud code function
Parse.Cloud.define("sendPush", function(request, response) {
var query = new Parse.Query(Parse.Installation);
query.equalTo('userObjectId', request.params.toUser);
query.exists("deviceToken");
Parse.Push.send({
where: query,
data: {
"alert": request.params.alert,
"sound": "default",
"type": request.params.type,
"status": request.params.status,
"fromUserId": request.params.fromUserId,
"fromUserName": request.params.fromUserName,
"messageId": request.params.messageId
}
}, { success: function() {
response.success("success");
}, error: function(err) {
response.error("error");
}
});
});
This function delivers pushes great if my iPhone is online. But if i turn off internet on my iPhone for 30 seconds and send push, this push is not delivered when i turn internet on. So i tried to set expiration date in my parse cloud code function to improve reliability of delivering pushes when device isn't online.
// Expiration_interval added
Parse.Cloud.define("sendPush", function(request, response) {
var query = new Parse.Query(Parse.Installation);
query.equalTo('userObjectId', request.params.toUser);
query.exists("deviceToken");
Parse.Push.send({
expiration_interval: 24 * 60 * 60,
where: query,
data: {
"alert": request.params.alert,
"sound": "default",
"type": request.params.type,
"status": request.params.status,
"fromUserId": request.params.fromUserId,
"fromUserName": request.params.fromUserName,
"messageId": request.params.messageId
}
}, { success: function() {
response.success("success");
}, error: function(err) {
response.error("error");
}
});
});
But it is still not working even if i turn off internet for just a 30 secs.
Thanks for any helps/ideas.
This is an artifact related to the Apple Quality of Service guidelines for the PUSH system.
In practice, it means that sometimes pushes will be missed. If they are crucial to your design, I suggest creating a database table of pushes that will deliver messages or data and have the app check on each load for new messages.
In practice, this means the push will not always get the attention of your users, but at least all the data will be delivered.
You can learn more at the parse.com help thread here: https://www.parse.com/questions/sending-push-notifications-quickly-causes-some-notifications-to-be-missed
As well as the Apple documentation here: http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html#//apple_ref/doc/uid/TP40008194-CH100-SW4
Related
I am sending the notifications using admin sdk.
This is my payload. I was able to set ttl (Time to Live) for Android but I am not sure how I can make it for iOS.
Basically if notification fails to send, then I dont want to resend it at all.
const payload = {
notification: {
title: 'New Appointments!',
body: '',
},
data: {},
android: {
ttl: 1000,
},
apns: {
payload: {
aps: {
badge: 1,
"sound":"default"
},
},
},
};
admin.messaging().send(payload).then((response) => {})
I think you're looking for apns-expiration:
A UNIX epoch date expressed in seconds (UTC). This header identifies the date when the notification is no longer valid and can be discarded.
If this value is nonzero, APNs stores the notification and tries to deliver it at least once, repeating the attempt as needed if it is unable to deliver the notification the first time. If the value is 0, APNs treats the notification as if it expires immediately and does not store the notification or attempt to redeliver it.
An example of this is seen in the FCM docs for ttl:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
},
"apns":{
"headers":{
"apns-expiration":"1604750400"
}
},
"android":{
"ttl":"4500s"
},
"webpush":{
"headers":{
"TTL":"4500"
}
}
}
}
The Problem I had was wrong format because I was reading the wrong documentation.
https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#ApnsConfig.
"apns":{
headers:{
"apns-expiration":"1604750400"
}
}
I have FCM integration in my Node.js project where I am sending number of notifications to the IOS users and I need to manage notification count i.e. Badge count which will be different across the devices but I am sending notification to a particular topic to which these devices are subscribed.
my Payload is :
var payload = {
notification: {
title: "Title...",
body: "Notification Body...",
sound: "customeSound.caf",
badge : "?"
},
data: {
testData: "custom data"
}
},
topic = "topicName";
admin.messaging().sendToTopic(topic, payload)
.then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
All devices that are subscribed to the corresponding topic will receive the same payload you set.
You'll have to send a separate payload for each device. Or if applicable, maybe just group the ones with the similar badge value -- but that would require you to send to a group of tokens (using registration_ids) instead of sending to a topic.
If you have the devices tokens and you want to send the badge counts to respective device token,
Instead of sending them one by one ( Which I personally think is not a good idea) the best alternative you can do is:-
You can achieve this by doing something like.....
First of all you need to query your DB to get the device token and badge count respectively
So say like you have and array
const badgeCounts = [{
device_token:'aaaaaaaaaaaaaaaaaaaaaa',
badge_count: 1
},{
device_token:'bbbbbbbbbbbbbbbbbbbbbb',
badge_count: 2
},{
device_token:'cccccccccccccccccccccc',
badge_count: 3
},{
device_token:'dddddddddddddddddddddd',
badge_count: 4
}]
Now you can map over this array and compose array of fcm messages, something like below :-
const fcmMessages = [];
badgeCounts.forEach((data) => {
fcmMessages.push({
token: data.device_token, //device token
apns: {
payload: {
aps: {
alert: {
title: "your title",
body: "your body",
},
badge: data.badge_count, // badge
contentAvailable: true,
},
},
},
data: {
// any payload goes here...
},
notification: {
title: "your title",
body: "your body",
},
});
});
/// in firebase messiging you can do like
messaging.sendAll(fcmMessages);
refer doc https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging-1#sendall
I'm trying to implement notifications using Firebase. The notification is received correctly when the app is in the background or foreground. So, the basic mechanics are working.
Now I've added Content Extensions and Service Extensions to the app. The Content Extension works when I use a local notification, but the Firebase message payload seems incorrect as far as the optional fields are considered. Here is a link to an image of my console:
And here is the Firebase remote notification payload that comes across (with some of the long Google numbers edited for anonymity:
{
aps =
{
alert =
{
body = "Eureka! 11";
title = "Patient is not doing well";
};
};
category = provider-body-panel;
gcm.message_id = 0:149073;
gcm.n.e = 1;
google.c.a.c_id = 2825604;
google.c.a.e = 1;
google.c.a.ts = 149073;
google.c.a.udt = 0;
mutable-content = 1;
}
It appears that the "category" and "mutable-content" are not in the correct place. They should be in the aps payload.
How can I get those options to be in the payload so that my app can correctly parse it and connect it with the Content and Service Extensions?
To start off, I'm going to mention that there are two types of message payloads for FCM. notification and data. See the documentation here
When sending notifications through the Firebase Notifications Console, it will be treated as a notification payload. However, if you add in Custom Data, it will add it in the payload as a custom key-value pair.
For example, in your post, the FCM payload should look something like this:
{
"notification": {
"body" : "Eureka!",
"title": "Patient is not doing well"
},
"data": {
"category": "provider-body-panel",
"mutable-content" : true,
"click_action" : "provider-body-panel"
}
}
What's wrong?
click_action should be inside notification.
mutable-content should be mutable_content (notice the underscore) and should be on the same level as notification.
(this one I might've misunderstood, but) There is no category parameter for FCM, click_action already corresponds to it.
See the docs for the parameters here.
It it is currently not possible to set the value for click_action and mutable_content when using the Firebase Notifications Console. You'll have to build the payload yourself, something like this:
{
"to": "<REGISTRATION_TOKEN_HERE>",
"mutable_content" : true,
"notification": {
"body" : "Eureka!",
"title": "Patient is not doing well",
"click_action" : "provider-body-panel"
}
}
Then send it from your own App Server. You could also do this by using Postman or cURL
"mutable-content should be "mutable_content" (keyword for firebase server to send as mutable-content for IOS) as you mentioned in your post, I think you left out in edit.
Below is an example with also the corrected format for the data section in the json sent to the FCM server.
So update would be:
{
"to" : "YOUR firebase messaging registration id here",
"mutable_content":true,
"notification": {
"title": "Its about time",
"body": "To go online Amigo",
"click_action": "NotificationCategoryIdentifier ForYourNotificationActions"
},
"data":{
"customKey":"custom data you want to appear in the message payload"
"media-attachment":"mycustom image url",
"catalogID":"mycustom catalog for my custom app"
}
}
Update Firebase Admin SDK and use sendMulticast(payload) method
var admin = require("firebase-admin")
admin.initializeApp({
credential: admin.credential.applicationDefault(),
});
// Create a list containing up to 500 registration tokens.
// These registration tokens come from the client FCM SDKs.
const registrationTokens = [
'YOUR_REGISTRATION_TOKEN_1',
// …
'YOUR_REGISTRATION_TOKEN_N',
];
// See documentation on defining a message payload.
var message = {
notification: {
title: '$FooCorp up 1.43% on the day',
body: '$FooCorp gained 11.80 points to close at 835.67, up 1.43% on the day.'
},
tokens: registrationTokens,
apns: {
payload: {
aps: {
'mutable-content': true, // use single quote
'category': 'INVITE_CATEGORY' // use single quote
}
},
},
};
// Send a message to the device corresponding to the provided
// registration tokens.
admin.messaging().sendMulticast(message)
.then((response) => {
if (response.failureCount > 0) {
const failedTokens = [];
response.responses.forEach((resp, idx) => {
if (!resp.success) {
failedTokens.push(registrationTokens[idx]);
}
});
console.log('List of tokens that caused failures: ' + failedTokens);
}
});
Ref: https://firebase.google.com/docs/cloud-messaging/send-message#send_messages_to_specific_devices
This worked for me with Cloud functions with Node.js
const payload = {
notification: {
title: name,
body: messageText,
badge: "1",
mutable_content: "true"
},
data: {
type: "MESSAGE",
fromUserId: name,
attachmentUrl: imageUrl
}};
I wrote a simple job to try sending a push notification to myself. Here's the code:
Parse.Cloud.job("testPush", function(request, status) {
Parse.Cloud.useMasterKey();
var installationQuery = new Parse.Query(Parse.Installation);
installationQuery.equalTo("user", "6t1JIuNqe1"); // I triple checked - this is the value of my user in the installation table.
Parse.Push.send({
where: installationQuery,
data: {
alert: "Test"
},
}, {
success: function() {
console.log("The Push Test Worked!");
status.success("All done with the push test!");
}, error: function(error) {
console.error("Something bad happened " + error);
status.error("Something bad happened during the Parse test...");
}
});
});
Although it logs in Parse that the job was run successfully, I never see a notification appear on my iPhone. I checked in Settings - it's all set up properly there (notifications are allowed to appear and should appear as banners, they should show up in notification center, they should show up on my lock screen). And yet the notification never appears.
What more do I need to check? What am I missing?
Pointer field should work with an instance.
Try replacing installationQuery.equalTo("user", "6t1JIuNqe1"); with the following:
var user = new Parse.User();
user.id = '6t1JIuNqe1';
installationQuery.equalTo('user', user);
Most of the times when I try to make a Push, none is beign sent. "Pushes sent" field in Push console tab displays 0. Despite that there are installations matching the query. When I uncomment this few lines below, installation is being returned successfully.
When trying to send Push manually via panel, when setting audience parameters, correct number of recipients is being displayed. After sending, once again "Pushes sent" displays ZERO.
Installations have deviceTokens [iOS].
That's my cloud code:
var installationQuery = new Parse.Query(Parse.Installation);
var inUserQuery = new Parse.Query(PKUser);
inUserQuery.equalTo("objectId", toUserID);
installationQuery.matchesQuery("user", inUserQuery);
// installationQuery.find({
// success: function(iresults) {
// console.log("inst results");
// console.log(iresults);
// }, error: function(err) {
// console.log("inst error");
// console.log(err);
// }
// });
// sending a push
Parse.Push.send({
where: installationQuery,
data: {
"alert": user.get("firstName")+": "+shortenedMessage,
"badge": "Increment",
"content-available": 1,
"text": message,
"from": user.id,
"id": object.id
}
}, { success: function() {
response.success("ok");
}, error: function(err) {
response.error("push");
}
});
When trying to use only the user ID for finding the users installation you should use a pointer object and passing that to installationQuery since equalTo on Parse.User compares the objectId no need for matchesQuery. If this doesn't work then you will need to check if the device tokens you have are valid for that phone+install since device tokens can change (ios 7 device token is different for same device).
var installationQuery = new Parse.Query(Parse.Installation);
var pointerUser = new Parse.User();
pointerUser.id = toUserID;
installationQuery.equalTo("user",pointerUser);
Parse.Push.send({
where: installationQuery,
data: {
"alert": user.get("firstName")+": "+shortenedMessage,
"badge": "Increment",
"content-available": 1,
"text": message,
"from": user.id,
"id": object.id
}
}).then(function(){
response.success("ok");
},function(){
response.error("push");
});