Not all subscribed iOS devices to topic receive data notification, most often none at all. Notification without data works as expected - ios

My app is implemented in React Native and is using Firebase for notifications. We have webhooks on the web app that are triggered on certain events. Those webhooks then send a request to the respective Firebase Cloud Function. With information from the request's header they get the topic they must send the notification to; from the request's body it gathers the information to be sent as data to the device. They are sent as data notifications only, the handler (using the React Native FCM API) then shows a local notification already translated with i18n using the react-native-push-notifications package. Everything works fine until I hit the weird situation described below.
Everything was doing alright both on Android and iOS until I launched the app on Test Flight for internal testing and then it stopped working after some time on iOS. Eventually, I noticed that when more than one iPhone subscribed to the same topic it eventually lead to inconsistencies in the delivery of the data notifications. The first iPhone to subscribe to the topic usually worked, the others didn't, the first one most of the time eventually stopped working as well or sometimes it just kept working while the others still didn't. I used the Firebase Console to send some test notification to the subscribed devices and it they actually received it. I then changed my Cloud Functions' code to avoid sending any data and just send some example body and title and it turns out the problem was here. As soon as I send something through the data field in the admin.messaging.Message object to be sent as argument to the admin.messaging().send method, they aren't received by the iPhone devices subscribed to the topic (or it's received by one or two max, the first ones to currently subscribe to the topic. But they usually stop receiving them after a while as well).
This is really really weird and being so inconsistent makes it practically impossible to debug with my current knowledge. Some things to keep in mind:
All Android devices still receive the notifications without a problem
I've watched the iPhone's console through Xcode to see if there was some error when processing the notification, in case they were actually getting the notification but they it failed before it was shown to the user. But nothing is logged by the SpringBoard process, making me conclude the notifications aren't actually getting to the device
I've manually sent notifications with cURL to APNs (with this guide). They were received fine
All notifications without data, regardless of the iOS specific apn headers, payload, etc, are received
What can be the cause of the problem? Or there's something in my code causing this strange behavior (which I doubt, since it works fine on Android and works fine in iOS as well on specific scenarios), there's some type of bug on Firebase's side causing some notifications to not be sent or, finally, there's some error on Apple's APNs side causing this. Highly doubt the last one, if the fault lays on any exterior factor it probably should be on Firebase's handling of topics.
Really would appreciate some help. Thanks in advance. Sorry If I didn't gave enough information, I actually never had the need to do a question on Stack Overflow. I'll leave below an example of a cloud function as well.
exports.orderPending = functions
.region("europe-west2")
.https.onRequest((req, res) => {
if (req.method == "POST") {
res.status("200").send("Webhook successful");
const topic = req.headers["code"];
const message = {
data: {
id: JSON.stringify(req.body.order.id),
event: "orderPending",
order: JSON.stringify(req.body.order),
},
topic,
android: {
priority: "high",
},
apns: {
payload: {
aps: {
contentAvailable: true,
},
},
headers: {
"apns-push-type": "background",
"apns-priority": "5",
"apns-topic": "APP BUNDLE ID HERE",
},
},
};
functions.logger.log(req.body.order.id);
functions.logger.log(topic);
admin.messaging().send(message)
.then((response) => {
console.log("Successfully sent message: ", response);
})
.catch((error) => {
console.log("Error sending message: ", error);
});
}
});

Turns out the data payload was exceeding the 4KB APNs limit but since when you send by topic it doesn't show any errors at all I had no way of knowing. So yeah, add this to your checklist

Related

Do I need APNs certificates if I'm using APNs Auth Key?

I'm writing an cross-platform app with Flutter and Firebase. I've been working on sending notifications, and it works perfectly on android. I send messages via the firebase admin functions sdk with no problems. The request looks like this:
const payload = {
notification: {
title: title,
body: body,
},
}
return admin.messaging().sendToDevice(tokens, payload, {
mutableContent: true,
contentAvailable: true,
apnsPushType: "background",
})
But when I try to send messages to iOS devices, I get the following error from the shell:
"error": {
"errorInfo": {
"code": "messaging/third-party-auth-error",
"message": "A message targeted to an iOS device could not be sent
the required APNs SSL certificate was not uploaded or
has expired. Check the validity of your development and
production certificates."
},
"codePrefix": "messaging"
}
I was under the impression that since I'm using a APNs key generated in the apple developer console, that I would not need any sort of SSL certificate. My key is uploaded to the Firebase console and iOS devices are successfully reporting their device tokens, so I'm not sure why I can't get a message through.
I've tried sending messages from the CLI and also from the FCM console online. Both have failed.
I've also enabled Push Notifications and Background Modes with background fetch and remote notifications.
Any help or suggestions would be appreciated.
This is really stupid, but the issue was an incorrect Bundle ID in firebase. When I first hooked up firebase it gave me a default bundle id (com.firebase.io or something dumb like that). Later when I actually put my app on testflight i called it something else entirely. So even though I followed all the directions on the FlutterFire website for setting up apple here it didn't work bc the bundle ids didn't match.
So I had to delete the firebase app, replace the googleservicesinfo.plist file (thought it might have been sufficient to just change the bundle id field in that file (idk for sure tho)) and then replaced it in the firbase console. Had to reupload the auth key too with the "new" app.
Hope that makes sense. Comment if you have any other questions!

FCM custom sound not working on iOS when in background mode

Did something change with FCM recently? Our apps use custom sound in push notifications. This has been working fine on both Android and iOS, whether in foreground, background, or closed. But recently something seems to have changed as the same PHP code we've been using suddenly stopped working for custom sounds when the app is closed or in background mode? (The notification is delivered, but it just uses the default device sound).
I was able to get it working again on Android by just including the sound files in /app/src/res/raw (we used to keep them there, but for a long time now it wasn't needed yet always worked... apparently it's needed again). Ok Android solved.
But for iOS it still has stopped working! I'm not sure if it's something I'm doing wrong in xCode or if I need to change the PHP code used to trigger the notifications. So here's both to review:
I am using:
xCode 11.6
iOS target 11.0
Testing on real device, iOS 13.6.1
Sound files are at {Project Name}/Resources/ and were added with "Copy items if needed" and "Create groups" both checked
Server side code to trigger notifications (which always worked fine before and has not been changed):
{
"notification": {
"title": "Test Title",
"body": "Test Body",
"sound": "sound_file.mp3",
},
"registration_ids": ["APA...g","fl...CS"]
}
Please help... thought I was past all of this years ago...ugggg!
Detail information:
https://medium.com/#ThatJenPerson/authenticating-firebase-cloud-messaging-http-v1-api-requests-e9af3e0827b8
Sample code:
{
"message":{
"token":"ewrTK.....",
"notification":{
"title":"Breaking News",
"body":"New news story available."
},
"data":{
"title":"Notification title",
"body":"Notfication body"
},
"apns":{
"payload":{
"aps":{
"sound":"notification.mp3"
}
}
}
}
}
We just touched base with the Firebase dev team and they were able to reproduce the issue. They have just created an internal ticket to fix this. Will follow up here on the resolution progress
After several additional rounds communication with Firebase, here is the current status update and their response:
I would like to let you know that this issue was just recently
identified as a bug with our Legacy API that’s not properly handling
the custom sound for iOS. Our FCM gurus have identified the fix for
this issue, and the fix is about to be released on our production, but
we can’t share any exact details or timeline for the release. The fix
should let you send notifications properly based on your custom sounds
configuration and there should be no code changes needed on your end.
Also, it should still properly handle the notification sounds if the
app is closed or in background state for your iOS application.
We are furthermore told that for the moment, migration from FCM HTTP to HTTP v1 is the only work around. Will post here again once we hear confirmation on the fix to the Legacy API (which most people reading this as using). Stay tuned...
We created a ticket with Google/Firebase support.
Their answer:
There's no ongoing issue on our end that may cause the custom sound to not work. However, by specifying a custom sound, the sound file must be on the user's device already, either in the app's bundle or in the library/Sounds folder of the app's container. You can refer to this documentation for more information.
For FCM, there isn't any extra sound processing, only passing the payload field, so as long as the payload on the client shows "sound" file is successfully passed down, there may be an issue with the setup. If there's no recent change for your application and the sound files, then this may be something caused on Apple's end. With that, it's best to check this with them.
We discovered that we apparently use the legacy HTTP API so we tested the newer HTTP v1 version. (described here: https://firebase.google.com/docs/cloud-messaging/migrate-v1 )
This solved the problem for us. Custom sound is back.
In the mean time Firebase support replied the following and now acknowledges there is a problem with the legacy HTTP API.
Thanks for sharing your observations, Mark.
We’ve received similar reports regarding this issue and that indicates
that this is not an unusual issue in relation to the increasing
reports on the Stack Overflow discussion that you’ve mentioned too.
With that, this issue was just recently identified as a bug with our
Legacy API that’s not properly handling the custom sound for iOS. Our
engineering team has identified the fix for this issue, and we
couldn’t provide a definite timeline at the moment for the release in
our production. I’ll let you know once I have an update for this
issue.
For now, you might consider using our HTTP v1 API to send your
messages. Apologies for the inconvenience this may have caused you.
On my side, no matter what sound name I set, I always receive on ios side "sound": "default"
Sending:
{
"notification": {
"title": "Alert",
"body": "Test Message",
"sound": "alert.aiff",
},
"registration_ids": ["APA...g","fl...CS"]
}
Receiving:
{
aps = {
alert = {
body = "Test Message";
title = Alert;
};
"content-available" = 1;
sound = default;
};
}
The only reason for this to happen is that FCM set sound to default while it redirect to APNS. Need ask google
Please check guys, Google has fixed this issue !!

Canceled SMS on cordova-sms/cordova-sms-plugin

I have been trying to work with https://github.com/cordova-sms/cordova-sms-plugin
For Android, it works as expected. Unfortunately, I can't capture the activity for ios and windows. It opens the Messages app (as we know) but it doesn't wait for the Messages response whether it has been sent, canceled, or failed.
It always response success. I am already using async (work on Android).
That's because I can't use the success and error parameters.
It only accepts 3 parameters:
sms.send(number, message, options);
It doesn't accept 5 parameters:
sms.send(number, message, options, success, error);
Error message is:
[ts] Expected 2-3 arguments, but got 5.
(method) SMS.send(phoneNumber: string | string[], message: string, options?: SmsOptions): Promise<any>
Has anyone able to capture Canceled SMS with this or other plugins?
P.S. I tried using other plugins but it can't even send.
According to your error-message (and your tags) you are using the cordova-plugin with ionic-native. In this case you do not need to pass a success/error callback because the ionic-team built a nice wrapper for it so it supports promise callbacks (docs for the .send() method).
To use it you have to modify your code as follows:
sms.send(number, message, options).then(
success => {
console.log(success);
},
error => {
console.log(error);
},
);
Please be aware that the callbacks only work correctly on iOS for this plugin. On the Android platform it will always return success immediately because of limitations of the operating system. For more see this FAQ on their github page.
0
Google changed the policy regarding to SMS access, so the direct reading of incoming SMS is no longer allowed and the associated permissions will be removed (SMS_READ).
Now, it is necessary to use the Android SMS Retriever API your SMS message needs to comply a specific format in order to be intercepted by your app.
In Cordova use this plugin to easily read incoming SMS:
cordova plugin add cordova-plugin-android-sms-retriever Github: https://github.com/diegosiao/cordova-plugin-android-sms-retriever

react-native: push notifications + parse

I am currently working on a small app project to learn and try out react-native on iOS. I have some experience with parse (parse.com) and would love to integreate parse in the new app. Currently, I have no problems including parse js into react-native. I am able to log in with accounts etc. Now I need to send push notifications to a certain number of users (not all users).
What I don't understand is how push notifications should work with react-native and parse. Usually, I would connect a device installation with a user ID and then send a push to a certain number of users (which means to the devices with the corresponding installation). The react-native guide (https://facebook.github.io/react-native/docs/pushnotificationios.html#content) doesn't mention anything like that. And even though it gives the parse guide as a reference, I fail to see how I should be able to send pushes via parse. The guide leaves a lot of information to be desired too. To what source do these "Listeners" subscribe to? From which server am I going to send notifications etc?
As I understand, parse js is not able to read the current installation. I hesitate to add Parse iOS to the project too. This feels unnatural and shouldn't be a required thing to do although it would allow me to read the current installation. (but still parse js is not able to register that installation in order to subscribe to push notifications).
At this point, I feel a little bit lost. This piece of information (https://news.ycombinator.com/item?id=9271687) tells me that it should be possible somehow. I just can't figure out how :(
Hope someone can help me with that. Some advice would be truely appreciated.
EDIT: react-native implements this behavior by default now. The interesting part is the event listener for the register event which now returns the device token. Procedure is pretty straight forward now. Just have a look at the docs Also check out the answer by JWindey. Has some very important points in them that are needed to actually trigger the events.
After a while and a lot of trying things out, we came up with an answer today. This is our solution and it seems to be working pretty good.
We use the following resources:
react-native
parse-js
Parse Rest API
react-native-remote-push (https://github.com/darylrowland/react-native-remote-push)
Follow the parse guide for push notifications (https://parse.com/tutorials/ios-push-notifications) and get everything set up correctly (profiles, certificates etc.). Using the react-native-remote-push component later on, you don't have to follow the steps 5 and 6.
Now add react-native-remote-push to you project. We had to make some minor adjustments to the code (mainly dealing with legacy objC code), but that may depend on your own project.
Our project has some kind of "starting page" that is shown every time the app is opened. On this page, we deal with push notification permissions as well as with the registration of the device token and the listener for push notifications. Our goal is to mimic the same behavior that we would receive with the parse iOS SDK.
We need to register the device and subscribe to a push channel first. react-native-remote-push allows us to handle the permissions and receive a device token. We then proceed to use this device token to register this installation via the Rest API. This code is part of our componentDidMount() call.
var PushManager = require('./RemotePushIOS');
var registerInstallation = require('./Installation');
componentDidMount() {
PushManager.requestPermissions(function(err, data) {
if (err) {
console.log("Could not register for push");
} else {
registerInstallation({
"deviceType": "ios",
"deviceToken": data.token,
"channels": ["global"]
});
}
});
PushManager.setListenerForNotifications(this.receiveRemoteNotification);
}
PushManager is the required component from react-native-remote-push and registerInstallation is the function containing the Rest API call.
/**
* registers an installation
* data should look like the following:
* {
* "deviceType": "ios", // or "android"
* // if android is targeted set
* // "pushType": "gcm",
* // "GCMSenderId": "56712320625545", // whatever the later means
* "deviceToken": "29e32a686fd09d053e1616cb48",
* "channels": [
* ""
* ]
* };
* for more information visit:
* https://www.parse.com/docs/rest#installations-uploading
*/
var registerInstallation = function(data) {
var url = "https://api.parse.com";
url += "/1/installations";
fetch(url, {
method: 'post',
headers: {
'Accept': 'application/json',
'X-Parse-Application-Id': PARSE_APP_ID,
'X-Parse-REST-API-Key': PARSE_REST_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(processStatus)
.then(parseJson)
.catch(error);
};
module.exports = registerInstallation;
"processStatus", "parseJson" and "error" are just some small functions that handle the result of the API call. I can provide more details if necessary. This function allows us to add a lot of information via the "data" object, such as userid, app version, parse version etc., just as you're used to from the iOS SDK. We only have a basic example working right now, but it should be easy to extend on this basis. This step was very important for us, because we need to associate every installation with a certain user.
You should be able to receive push notifications by now. You can handle them in a "receiveRemoteNotification" function that acts as a listener. A basic function is provided on the website of the react-native-remote-push component.
I hope I could share some insight about this topic. If I should go into more detail on certain parts, I'll gladly add some more information.
I did some investigation with the Parse + react-native combination and have it working.
You have to add the Parse SDK (follow the guide) to your project, and link all the necessary libraries.
Don't forget to add the steps on point 5: https://parse.com/tutorials/ios-push-notifications.
Then add RCTPushNotificationManager.h + m (from react-native/Libraries) to your project. After that, add the following in AppDelegate.m:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[NSNotificationCenter defaultCenter] postNotificationName:#"RemoteNotificationReceived"
object:self
userInfo:userInfo];
}
That should do it.
The official PushNotificationIOS has register event, which we can obtain token from. So, with having #MrMuetze's REST API, I could install the device to Parse.
PushNotificationIOS.addEventListener('register', function(token){
registerInstallation({
"deviceType": "ios",
"deviceToken": token,
"channels": ["global"]
})
});

Some Apple Push Notifications from Azure Mobile Services not Arriving

I've been struggling with this for a few days and am stuck. I am using Azure Mobile Services and have two scheduler scripts defined. One is very simple and just sends all device tokens for User ID #1 a test push notification:
function SendTestNotification() {
sendPushNotification(1, 'SendTestNotification # ' + new Date());
function sendPushNotification(userId, body) {
var users = tables.getTable('NotificationTokens');
users.where({
User_Id: userId
}).read({
success: function(tokens) {
tokens.forEach(function(token) {
var alert = {
badge: 0,
alert: body,
sound: 'default'
};
push.apns.send(token.Token, alert,
{
error : function(err) {
console.error(err);
}
});
});
},
error: function(error) {
console.error(error)
return;
}
});
}
}
I have a second scheduler script which is much more elaborate and contains the actual push notification logic for my app. It contains the exact same sendPushNotification() function shown above. This 2nd script also contains an additional call to send me a "test" notification.
So all told, with these two scripts setup and scheduled, my iOS devices should get a total of 3 push notifications every 15 minutes. I have them timestamped as well so that they show when the notification was sent from the server.
However, when I run these scripts either manually or just let them run on their own every 15 minutes, I almost never get all of the notifications. Sometimes I get none. Sometimes I only get the first test notification. Sometimes I get the first test notification, the "real" notification, but not the 2nd test notification (which should have been sent by the exact same code that sent the "real" notification).
I have checked the logs on the portal and there are no errors coming back from APNS. I know the logging is working as I've seen errors for invalid device tokens in the past.
I should also state that I've sent these devices push notifications via APNS using a simple C# application with absolutely no problem.
Can anyone give any suggestions or see what I may be doing wrong? I have an app waiting to be reviewed and would love to get this issue taken care of.
Last time this happened to me I changed the scale from Free to Basic, and all notification were sent.
You said that there is no error, but just remember, if an error occurs you have to start over again from the point you stopped because the apns close the connection once an error occurs.

Resources