Push notifications via FCM server send not received by all iOS devices - ios

We have an issue with push notifications sent to a topic from our server. Actually, it isn't the sending part, it is the receiving. That push notifications are received by some iOS devices but not all. We haven't had issues with Android devices, we had one report regarding Android but I think that's a hoax.
Technical details
We build a request in PHP:
$topic = '/topics/urgent_de';
$data = [
'to' => $topic,
'priority' => 'normal',
'notification' => [
'title' => $pushNotification->title_de,
'body' => $pushNotification->text_de,
'topic' => 'urgent',
'target' => $pushNotification->deeplink
],
'data' => [
'title' => $pushNotification->title_de,
'message' => $pushNotification->text_de,
'topic' => 'urgent',
'target' => $pushNotification->deeplink
]
];
Devices register on this topic using:
let sub:SupportedLanguage = UrlHelper.getBestSupportedLanguage() == .de ? .de : .en
static let topicUrgent = "/topics/urgent"
static let topicUrgentLocalizedPrefix = "urgent_"
let localizedUrgent = "\(Constants.Push.topicUrgentLocalizedPrefix)\(sub.rawValue)"
Messaging.messaging().subscribe(toTopic: localizedUrgent)
LoggingHelper.info("Subscribed to topic: \(localizedUrgent)")
Messaging.messaging().subscribe(toTopic: Constants.Push.topicUrgent)
LoggingHelper.info("Subscribed to topic: \(Constants.Push.topicUrgent)")
This means, each device subscribes not only to a localized but also a general topic.
Problem description
We sent out a few pushes within two weeks last September and some devices in our company received them, some didn't. It didn't depend on their app or iOS version. They all have given permission to receive pushes. They were all online and running when we sent that push. None of the devices had our app in foreground. Some haven't started the app for a few weeks some had them open that same day.
What we already tried
We tried priority high
We tried to find out how many users are affected. But reports in firebase console don't seem to include iOS devices or firebase can't track if a push was received by iOS devices (when setting filter "platform/channel" to "iOS" or "app" to "iOS app", received number is "0" but sent to several thousand)
We put an iOS device which didn't receive one push on our development Macbook
We tried to check logs but (of course) there was not output
Then we deployed from XCode and sent a push to that device's token but it was received => We couldn't reproduce the issue.
We then uninstalled the development version and installed app from app store.
The following push was received
We sent another push to topic a few months later. It wasn't received.
I searched stackoverflow and other resources but I didn't find this specific issue (usually it is a problem where no pushes are received or similar)
All in all, when we look at all devices in our company, only half of them receive those push notifications.

This is a simple check, but worth trying.
Please check that you have enabled push notifications on the devices that don't receives the push notifications.
Also, please check that "Authorization" request for notifications on iOS handles different iOS versions (Swift/iOS) because they are not the same, it may vary.
Let me know...

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!

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

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

Displaying notifications on iOS device handled by a Ruby on Rails web application

I'm building an iOS app that needs to have notifications display every time any user of that app does something like send another user a message. What I have currently is a working Ruby on Rails and Android app. The Rails app notifies Firebase when a notifiable activity occurs like so:
if active_time.save
fcm = FCM.new("AIzaSyDhMXi3797t2oZMxOTo-Nph8IoypRL8Ooc")
registration_ids = Array.new
for user in group.users do
if not user.token.nil?
registration_ids.push(user.token)
end
end
options = {data: {score: "123"}, notification: {body:"Active time added to " + group.name, title: "Groupsync"}}
response = fcm.send(registration_ids, options)
active_time.delay(run_at: active_time.start.getutc).send_group_live_notification
What I'm unsure about is how to get my iOS client to notice these activities and display notifications like its Android counterpart. I have set up FCM on the iOS client and am able to send notifications, just not sure what the next step is in terms of making the link.
EDIT: I should mention for the above piece of is controlled by my iOS client through HTTP POST requests
I recommend you using a service line OneSignal. It's free and you can set a lot of platforms there, between android, ios and web browsers. For ios you're gonna need a push certificate from apple (see more here: https://medium.com/#ankushaggarwal/generate-apns-certificate-for-ios-push-notifications-85e4a917d522). Once you set your configs for android and ios, for example, you can use their api and send the notifications using their ruby gem, for example (https://github.com/tbalthazar/onesignal-ruby).
Good luck!

What are the possible reasons to get APNs responses BadDeviceToken or Unregistered?

When sending notifications to iOS users, for some of them I get response status code 400 (BadDeviceToken) or code 410 (Unregistered).
From Apple documentation about "BadDeviceToken":
The specified device token was bad. Verify that the request contains a valid token and that the token matches the environment.
What is the meaning of "bad"? I know for a fact that the device token was valid at some earlier time. What does a user do to make its device token bad?
From documentation about "Unregistered":
The device token is inactive for the specified topic.
Does this necceserally mean that the app has been deleted? Or there can be some other reasons for this response.
As you've quoted from Table 8-6 in the APNS documentation, there are two possible causes for the error:
That the device token is invalid
That the device token does not match the environment
If it is the first case, make sure that the iOS app registers the device for remote notifications every single time that the app is launched because there are many reasons for the device token to change across launches, as outlined in Configuring Remote Notification Support.
If it is the second case, you need to be sure that:
The backend uses development configurations if your app build was signed with development APNS entitlements, and
The backend uses production configurations if your app build was signed with production APNS entitlements.
Luckily, as the iOS developer, you don't need to directly change the APNS entitlements yourself. It is always in development, and is only automatically changed by Xcode to production when you generate the build and export for App Store or enterprise distribution. As for the backend, your backend developer should know how to configure the backend for development and production environments. For some frameworks, it is a matter of toggling some boolean named isProduction. Ultimately, according to Communicating with APNs under the section APNs Connections, push notifications are sent to different APNS endpoints depending on whether the environment is production or development.
Let's pretend that the BadDeviceToken error is due to the second case--that the device token registered by the app does not match the backend's properly configured development environment. First, in your Xcode project, check your .entitlements file and verify that the APS Environment key's value is development. It should look like this:
Next, after you generate an archive, open the Organizer (via the Window menu > Organizer), select the archive, and click on Export... at the right. You should see four methods of distribution:
If you select App Store or Enterprise, you will see in the later dialogs that Xcode changes the APNS entitlements to production (see tip of red arrow):
If you select Ad Hoc or Development, the text under aps-environment will be development, which should then match the backend's configurations.
I was sending a "development" device token to the "production" apple push servers. I fixed it by sending requests to api.development.push.apple.com instead of api.push.apple.com
Status code '400' : You get this error when you try to send the notification with a wrong certificate. Make sure that you use production certificate for production environment. It is bad because you are using bad configurations.
Status code '410' : Yes with this code we can understand that App has deleted. In our app when we get this status code we delete this token from db. The other scenario could be that user has reinstalled the app which could change his token. So its better you remove this token.
Error code 404: BadDevice token
Posssible reasons:
Your .pem certificate may be wrong.
Your BundleId may be wrong.
Your Device id may be wrong.
Note: Append .voip with your bundleid for sending voip push notification(exmple: bundleid.voip)
Here is an workable example of voip push notification :
<?php
$token = $_REQUEST['tok'];
if (!defined('CURL_HTTP_VERSION_2_0')) {
define('CURL_HTTP_VERSION_2_0', 3);
}
// open connection
$http2ch = curl_init();
curl_setopt($http2ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
// send push
$apple_cert = 'certificate_name.pem';
$message = '{"aps":{"action":"message","title":"your_title","body":"your_message_body"}}';
$http2_server = 'https://api.development.push.apple.com'; // or 'api.push.apple.com' if production
$app_bundle_id = 'your bundle id';
$status = sendHTTP2Push($http2ch, $http2_server, $apple_cert, $app_bundle_id, $message, $token);
echo $status;
// close connection
curl_close($http2ch);
function sendHTTP2Push($http2ch, $http2_server, $apple_cert, $app_bundle_id, $message, $token)
{
// url (endpoint)
$url = "{$http2_server}/3/device/{$token}";
$cert = realpath($apple_cert);
// headers
$headers = array(
"apns-topic: {$app_bundle_id}",
"User-Agent: My Sender"
);
curl_setopt_array($http2ch, array(
CURLOPT_URL => $url,
CURLOPT_PORT => 443,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $message,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSLCERT => $cert,
CURLOPT_HEADER => 1
));
$result = curl_exec($http2ch);
if ($result === FALSE) {
throw new Exception("Curl failed: " . curl_error($http2ch));
}
// get response
$status = curl_getinfo($http2ch, CURLINFO_HTTP_CODE);
if($status=="200")
echo "SENT|NA";
else
echo "FAILED|$status";
}
?>
If using node-apn. I found the answers here rather confusing as APN has moved to using tokens which work for either sandbox or production modes. I was also confused because writing a 1 off script to send a notification worked in production.
It wasn't until I started to suspect that my my service wasn't getting process.env.NODE_ENV === 'production'. So I added that to my startup log message and low and behold my service runner forever was not getting any environment variables. Because of this, it was trying production device ids on the sandbox url.
Feedback is no longer immediate with 410 notifications. Apple intentionally has done this due to privacy concerns. It may take up to a week now. See https://developer.ibm.com/customer-engagement/2019/05/01/apns-feedback-service-change/

Google Cloud Messaging showing success message but not sending iOS

So I have run into a very strange problem with Google Cloud Messaging. The problem I am having is that it is registering the devices successfully, and when a message is sent I get a success message from Google. But the devices never receive any messages.
The message I get back from GCM is:
"result": "Push notification sent successfully: {\"multicast_id\":6008387530769664000,\"success\":1,\"failure\":0,\"canonical_ids\":0,\"results\":[{\"message_id\":\"0:1442824842607522%73fc535e73fc535e\"}]}"
To make things even more confusing, my implementation was working about 2 weeks ago and I have not changed anything to date. The Android version of the app is receiving messages with no problems it is only the iOS implementation that is not working.
Any help would be much appreciated!
Thanks!
So I finally solved this issue after of pulling the last remaining hairs out of my head.
It turns out the devices are receiving the messages but GCM sets the priority to the lowest priority by default. This means the device receives the notification but never displays it. This priority is used for silent notifications to wake the app up in the background. I discovered this because I kept receiving the message in the console saying:
Low Priority Push: [com.test.app] - Background Refresh Not Supported
Priority is a value between 1 and 10 so I then set the priority to 10 and got the message instantly on the device. My GCM POST request body now looks like this:
{
"to": "GCM token here",
"notification": {
"sound": "default",
"badge": "2",
"title": "default",
"body": "Test Push!",
},
"priority" : 10,
}
I really hope this helps others as I have spent a week pulling my hair out regarding this.
(ノಠ益ಠ)ノ
EDIT:
You can set "priority" to "high" and that works exactly the same as setting it to "10" (priority is a value between 0 and 10. Google coverts the text to the number for iOS
Instead of adding "priority" : 10,
You should add the following line:
"content_available" : true,
In APNS server (iOS), The content_avaialble changes to 1 which leads the push notification in background. And adding "priority":10, will drain more iphone battery. In my case I don't even have anything related to priority, but it still works.

Resources