How to create APNS payload for Firebase? - firebase

I have connected my IONIC 4 app to Firebase and then to AWS SNS.
Everything works perfectly for Android, the push notifications are delivered.
But iOS devices never receives them. I have made all the necessary configs, certificates and keys. When I publish a test message via Firebase Console, the iOS devices receive the notifications.
I get a little confuse about this payload object and I guess it might be causing this issue.
The payload object (ignore the values):
var payload = {
default: "Trokaí",
GCM: {
data: { message: "A", payload: "", title: "C" },
notification: {
message: "E",
payload: "F",
title: "Trokaí",
body: msgPush
}
},
APNS: {
aps: {
alert: {
title: "Game Request",
body: "Bob wants to play poker",
"action-loc-key": "PLAY"
},
badge: 5
},
acme1: "bar",
acme2: ["bang", "whiz"]
}
};
payload.GCM = JSON.stringify(payload.GCM);
payload.APNS = JSON.stringify(payload.APNS);
payload = JSON.stringify(payload);
The publishing method:
sns.publish(
{
Message: payload,
MessageStructure: "json",
TargetArn: device.endpoint
},
function(err, data) {
if (err) {
return;
}
}

Related

Flutter - Background notifications not working in iOS

I'm creating a public chat app in Flutter, and I can't receive background notifications on iOS, when I trigger the method admin.messaging().send(payload) from my Cloud Function.
Here's my payload in the Cloud Function :
var payload = {
notification: {
title: `My Title`,
body: `My message`,
},
android: { priority: "high" },
apns: {
payload: {
aps: {
contentAvailable: true,
},
},
headers: {
"apns-push-type": "background",
"apns-priority": "10",
"apns-topic": "io.flutter.plugins.firebase.messaging", // bundle identifier
},
},
topic: `mytopic`,
};
I tried a bunch of different payloads :
I tried notification + data
I tried only data (no notification) (in that case, I have to locally display the notification, but it is not working because the function FirebaseMessaging.onBackgroundMessage is never called)
I tried to change "apns-priority" to 5
I tried using a token instead of a topic
I set up carefully all I need to configure, thanks to the official documentation :
https://firebase.flutter.dev/docs/messaging/usage/
I also checked Github issues, like these ones :
1041, 6112, 5988, 1644, 4300, 4097
What is working :
Background and foreground notifications on Android and iOS, when sent from Firebase Console (in 'Cloud Messaging' section)
Background and foreground notifications on Android only, when sent from Cloud Function
Foreground notifications on iOS, when sent from Cloud Function
What is NOT working :
Background notifications on iOS when sent from Cloud Function
When app is terminated, it does not work as well.
I finally resolved this issue ! 🎉
I simply removed the line "apns-push-type": "background" in my payload.
Now it is working.
It appears that the line "apns-topic" was useless as well.
Here is my final payload :
var payload = {
notification: {
title: `# ${context.params.passion}`,
body: `${newMsg["senderPseudo"]} ${
type == "image" ? "a envoyé une image." : `: ${newMsg["message"]}`
}`,
},
// Set Android priority to "high"
android: {
priority: "high",
},
// Add APNS (Apple) config
apns: {
payload: {
aps: {
contentAvailable: true,
},
},
headers: {
//"apns-push-type": "background", // This line prevents background notifications to be displayed
"apns-priority": "10",
},
},
token: "dnqTQVso60GfnnuOjHv8_e:APA91bElr-K3xkQMdYHX8VMrMZNCYCjO4zJlGseRh25AS_GT7cg9zlOGdQl4KXvr88ypeWjZjrPzrLRHitsQ-JKQK057ZQb_36c_lfsNjHXbYMYI2iS3jV_HGWf7Ene-ZlPvOb0aRr8u"
};

How to collapse a Web Push notification sent by FCM

I'm using FCM with firebase-admin to send a Web Push notification for multiple clients. I want to replace an older message by a new one.
This is how I'm calling it:
return await admin.messaging().sendMulticast({
tokens,
notification: {
title: "Message Title",
body: "Message body.",
},
data: {
type: "ring",
callId,
},
webpush: {
fcmOptions: {
link: process.env.PWA_APP_URL,
},
headers: {
Urgency: "high",
},
},
android: {
collapseKey: "ring",
priority: "high",
ttl: 10,
notification: {
color: "#ff0000",
defaultSound: true,
sound: "default",
lightSettings: {
color: "#ffcc00",
lightOffDurationMillis: 1000,
lightOnDurationMillis: 1000,
},
tag: "ring",
vibrateTimingsMillis: [1000, 1000],
visibility: "public",
priority: "max",
},
},
});
When the message arrives to an Android app client it's collapsed because of android.collapseKey property. But I can't get the same behavior in my web application.
According to the docs, there's a Topic option for Web notifications, but I'm not sure where to put it. I tried to use it as webpush.headers.Topic property but with no success. Messages are not collapsed in Android/Chrome.
What am I doing wrong?
I've followed collimarco's tip and could use showNotification method to collapse a notification.
In firebase-admin/server code I had to remove the notification fields in the message's payload. Now it's just a data message:
return await admin.messaging().sendMulticast({
tokens,
data: {
type: "ring",
callId,
},
});
In the web application I created a background message handler. Now I can call showNotification method when I new message arrives using tag property with renotify=true.
messaging.setBackgroundMessageHandler(function (payload) {
// ...
return self.registration.showNotification("Message Title", {
body: "Message body.",
tag: "ring",
renotify: true,
vibrate: [1000, 1000],
});
});
It would be great to have these properties set on the message payload only.

Error while making request: socket hang up. Error code: ECONNRESET

I'm using node.js as a backend server for sending push notification from the Firebase Cloud Messaging service. The notifications are working fine with local server but on live server, I get this error:
Error while making request: socket hang up. Error code: ECONNRESET
Things to consider are that...
Number of users are in the thousands on live server
Firebase version is firebase-admin#6.5.1
Previously unregistered tokens are still there. But now registered tokens are being stored.
This is my code for sending notifications:
for (let c = 0; c < tokens.length; c++)
{
let notifyTo = tokens[c];
const platform = platforms[c];
let payload;
if (platform === "ios") {
payload = {
notification: {
title: "title",
subtitle :"messgae",
sound: "default",
badge: "1"
},
data: {
sendFrom: "",
notificationType: "",
flag: "true"
}
};
} else if (platform === "android") {
payload = {
data: {
title: "",
message : "",
flag: "true"
}
};
}
const registrationtoken = notifyTo;
await admin.messaging().sendToDevice(registrationtoken, payload)
.then(function (response) {
console.log("Successfully sent message:");
})
.catch(function (error) {
console.log("Error sending message: ");
});
}
Your issue is caused by your function taking too long to respond to the client (more than 60 seconds) and is caused by the following line:
await admin.messaging().sendToDevice(registrationtoken, payload)
Because you are waiting for each call of sendToDevice() individually, you are running your for-loop in synchronous sequential order, rather than asynchronously in parallel.
To avoid this, you want to make use of array mapping and Promise.all() which will allow you to build a queue of sendToDevice() requests. As in your current code, any failed messages will be silently ignored, but we will also count them.
Your current code makes use of two arrays, tokens and platforms, so in the code below I use a callback for Array.prototype.map() that takes two arguments - the current mapped value (from tokens) and it's index (your for-loop's c value). The index is then used to get the correct platform entry.
let fcmPromisesArray = tokens.map((token, idx) => {
let platform = platforms[idx];
if (platform === "ios") {
payload = {
notification: {
title: "title",
subtitle :"messgae",
sound: "default",
badge: "1"
},
data: {
sendFrom: "",
notificationType: "",
flag: "true"
}
};
} else if (platform === "android") {
payload = {
data: {
title: "",
message : "",
flag: "true"
}
};
}
return admin.messaging().sendToDevice(token, payload) // note: 'await' was changed to 'return' here
.then(function (response) {
return true; // success
})
.catch(function (error) {
console.log("Error sending message to ", token);
return false; // failed
});
});
let results = await Promise.all(fcmPromisesArray); // wait here for all sendToDevice() requests to finish or fail
let successCount = results.reduce((acc, v) => v ? acc + 1 : acc, 0); // this minified line just counts the number of successful results
console.log(`Successfully sent messages to ${successCount}/${results.length} devices.`);
After this snippet has run, don't forget to send a result back to the client using res.send(...) or similar.

firebase - How to know which registration_id is bad/expired/deleted

So I am pushining notifications in a batch to multiple devices. I want to delete device GFCM tokens from my databse if they are not in firebase.
Test request:
{"registration_ids": ["fznIum-OIL0:APA91bE9n6gxqVEB3hR0On_TZqH_cOOO7rFyLE5MYbvKXUX3737IonXquZRoHeY1UJf20G4nrahz35p80X8PAPouviYvXgtekAzeDFuwaWyiPI1sFlN1d1wGp5OulXv9wUzHyegeZiih","dsasadadsasdasdasdasdefs","fjb0qTB9ACI:APA91bHvx0pSlq-NuQz-bU70DhL7BXAcd-DqT9YT0ZjYw7R_pkBmr8oe6XdNw8IPlU67d-rXMfRY3mybaO7K5GHo_NH7G-Ya1Ae2xxJP1JIbuuSYomz31y_MPQijFK8w-bZXQ26SWE09"],
"notification":{
"body":"Yoou have a new message from Alice"}}
And response:
{
"multicast_id": 7971310728176484096,
"success": 1,
"failure": 2,
"canonical_ids": 0,
"results": [
{
"error": "NotRegistered"
},
{
"error": "InvalidRegistration"
},
{
"message_id": "0:1481547993513035%4f9c20964f9c2096"
}
]
}
How to detect which registration_ids are bad/Not registered?
Thanks!
Okey, i think i figured it out.
"results": Returns responses/errors in the order you specified it in registration_ids
when you are sending push notification to any mobile,you can find in err that registartion_ids/token isbad /not registered/
var serverKey = 'sdasd';//server key
var fcm = new FCM(serverKey);
var token = "token";//token here
var message = {
to: token,
collapse_key: 'your_collapse_key',
notification: {title: 'hello', body: 'test'},
data: {my_key: 'my value', contents: "stackoverflow.com"}
};
fcm.send(message, function (err, response) {
if (err) {
console.log(err);//find here registration_ids are bad/Not registered?
} else {
console.log(response);
}
});
});

HTTP.post to FCM Server not working

I am using Ionic 2 with HTTP native module to make a post request to FCM server for push notifications. The code I am using is:
HTTP.post(
"https://fcm.googleapis.com/fcm/send",
{
"notification": {
"title": "Notification title",
"body": "Notification body",
"sound": "default",
"click_action": "FCM_PLUGIN_ACTIVITY",
"icon": "fcm_push_icon"
},
"data": {
"hello": "This is a Firebase Cloud Messagin hbhj g Device Gr new v Message!",
},
"to": "device token",
},
{
Authorization: {
key: "AUTHORIZATION KEY HERE"
}
})
Its giving me an error:
Unimplemented console API: Unhandled Promise rejection:
Unimplemented console API: Error: Uncaught (in promise): [object Object]
I tried the post request with Postman, it works perfectly fine delivering push notifications.
The code with Postman is:
POST /fcm/send HTTP/1.1
Host: fcm.googleapis.com
Content-Type: application/json
Authorization: key=Authorisation Key
Cache-Control: no-cache
Postman-Token: 446e253b-179a-d19b-21ea-82d9bb5d4e1c
{
"to": "Device Token",
"data": {
"hello": "This is a Firebase Cloud Messagin hbhj g Device Gr new v Message!",
}
"notification":{
"title":"Notification title",
"body":"Notification body",
"sound":"default",
"click_action":"FCM_PLUGIN_ACTIVITY",
"icon":"fcm_push_icon"
},
}
Questions:
I am unable to add content-type to the header in the HTTP post request, but it works with postman.
If I try to add a function(response) { to get the response from the server, it gives me an error. The documentation for the same is at https://github.com/wymsee/cordova-HTTP
why are you using HTTP native module? Angular has a built in Http.
Using this one (importing HttpModule from #angular/http in your NgModule) you can just call
import { Http, Headers } from '#angular/http';
......
constructor(public http: Http) { }
sendPushNotification(deviceId: string) {
let url = 'https://fcm.googleapis.com/fcm/send';
let body =
{
"notification": {
"title": "Notification title",
"body": "Notification body",
"sound": "default",
"click_action": "FCM_PLUGIN_ACTIVITY",
"icon": "fcm_push_icon"
},
"data": {
"hello": "This is a Firebase Cloud Messagin hbhj g Device Gr new v Message!",
},
"to": "device token"
};
let headers: Headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'key='+this.someKey
});
let options = new RequestOptions({ headers: headers });
console.log(JSON.stringify(headers));
this.http.post(url, body, headers).map(response => {
return response;
}).subscribe(data => {
//post doesn't fire if it doesn't get subscribed to
console.log(data);
});
}
push.on('notification', (data) => {
if (data.additionalData.foreground) {
// if application open, show popup
let confirmAlert = this.alertCtrl.create({
title: data.title,
message: data.message,
buttons: [{
text: 'Ignore',
role: 'cancel'
}, {
text: 'Go to',
handler: () => {
//TODO: Your logic here
this.navCtrl.setRoot(EventsPage, {message: data.message});
}
}]
});
confirmAlert.present();
} else {
//if user NOT using app and push notification comes
//TODO: Your logic on click of push notification directly
this.navCtrl.setRoot(EventsPage, {message: data.message});
}
});
push.on('error', (e) => {
alert(e);
});
});

Resources