We are having trouble using the new iOS Critical Alerts feature with OneSignal. The app we have is built using Apache Flex and the push notification payloads are being compiled/generated through Distriqt ANE and sent through the OneSignal iOS SDK. We have been approved by Apple for Critical Alerts but when attempting to send the payload through OneSignal, it does not work.
The Distriqt team has built in support for Critical Alerts, which is great, but once it is sent through OneSignal, the payload isn't delivered successfully and the critical alert is not generated. To be clear, general push notifications work fine.
OneSignal has documentation on Critical Alerts, though it is over simplified. The payload needed to trigger Critical Alerts on iOS is clear but there is no way to construct a payload like this through the OneSignal iOS SDK.
In the link, we have performed #1 (Update OneSignal App Payload Structure for iOS) but #2 is unclear. What does the payload need to look like? The code provided does not make that clear. Additionally, what is the referenced 'notification category extension' and if that code is identified, what are we needing to do at that point?
We have contacted OneSignal and have been unsuccessful in understanding how to successfully construct this payload. The documentation says 'iOS features OneSignal SDK supports' but from their support we received this:
Unfortunately our SDK is not setup to handle critical alerts.
One user reported he achieved this with our additional data parameter like this:
"data": {"CRITICAL_ALERT":"YES","CRITICAL_PAYLOAD":{"CRITICAL_VOLUME":"1.0","CRITICAL_SOUND":"Alert.wav"}}
Though that doesn't work. Although a relatively new feature, I am hoping someone in the community has been successful at this and can help. Thanks!
TL;DR Building an iOS Notification Service Extension is the solution. The OneSignal documentation mentions this but in far less detail.
When Critical Alerts was introduced by Apple, they changed the sound parameter that is passed through with an APNS payload. Previously, the sound was only ever a string. For example:
{
“aps” : {
“sound” : “critical-alert-sound.wav”
}
}
With Critical Alerts, it is a dictionary. Example:
{
“aps” : {
“sound” : {
“critical”: 1,
“name”: “critical-alert-sound.wav”,
“volume”: 1.0
}
}
}
The string version is still valid and is used for non-critical alerts. Based on inference and testing, when passing the sound parameter to OneSignal, it only supports the initial 'string' version. When OneSignal sends the payload to Apple, it passes it as a string thus even trying to pass a sound dictionary to OneSignal won't work because it gets parsed down before it gets to Apple. By the time Apple communicates back with your device, the dictionary is gone, thus preventing the device from ever recognizing it as a Critical Alert.
This is where the Notification Service Extension comes in. From Apple's documentation:
A UNNotificationServiceExtension object provides the entry point for a Notification Service app extension, which lets you customize the content of a remote notification before it is delivered to the user. A Notification Service app extension doesn't present any UI of its own. Instead, it is launched on demand when a notification of the appropriate type is delivered to the user’s device. You use this extension to modify the notification’s content or download content related to the extension.
In short, you can intercept the notification JSON payload coming from Apple and modify it just before the user sees it.
In order for the information to be passed to the device correctly from OneSignal->Apple->Device, you need to:
1) Set the additional_data_is_root_payload value on your OneSignal account to true. This is done through an Update an App API call on OneSignal. I used Postman for this. To be clear, this is needed to be done one time, it is not something needed to be repeated every time you make an notification API call.
2) In your OneSignal API payload, set the mutable_content parameter to true.
As mentioned above, you can use values in the OneSignal data parameter of the payload. The data payload is an open field that can be used for any additional information you want to pass through the OneSignal->Apple->Device flow and does get delivered to the device/app which you can then parse however you would like. In my example, we use:
"data": {"CRITICAL_ALERT":"YES"}
The data payload is arbitrary, it just needs to match the checks you do in the Notification Service Extension.
We then create our Notification Service Extension in XCode. There are some great step-by-step instructions here on creating an extension. This is done through XCode so if you are building a native app, it's simply done through your XCode project for your application. If you are using a framework like Adobe AIR, it is quite a bit more complicated and I will detail that in a different post. Keep in mind that the Notification Service Extension is basically a separate 'app' bundled with the parent app. It is compiled into a file with extension 'appex' and even though it is a separately bundled binary, it is specifically targeted to your parent app.
Once the extension is created, your XCode project will have a file named NotificationService.swift with a class that has the didReceive method within it. We then added this code:
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
if ((request.content.userInfo["CRITICAL_ALERT"] as? String) == "YES"){
bestAttemptContent.sound = UNNotificationSound.defaultCriticalSound(withAudioVolume: 1.0)
}
contentHandler(bestAttemptContent)
}
You can see that we check the data payload (request.content.userInfo) to see if the CRITICAL_ALERT key is set with a value of YES. If so, we add a 'critical sound' to the notification which effectively turns it into a critical alert. In this case, we kept it simple with the defaultCriticalSound function but you can also use criticalSoundNamed if you want to define your own sound. You could also pass the sound you want to use for the alert through the data payload and then parse it and add it in the Notification Service extension if you wanted to create specific sounds for specific notifications.
At this point, we tested this through the OneSignal interface once we had deployed the app to our devices. This can be accessed by logging into your OneSignal account and then going to 'Messages' -> 'New Push' and choosing the devices you would like to send the push to:
Hopefully this is helpful to others using OneSignal for Critical Alerts (and hopefully they update their system soon to no longer need a Notification Service Extension).
Related
So I'm using the firebase push notification in my app. Notification is working fine with the following payload,
{
"to": "cMXdSK_ud0LpqMoju85FvT:APA91bFMke92QR1IvcLeLhG5XrvwcE0OfSLGpJW1ds9-FFDornoeorMlKOn6IEtDYsuvwlRrwJnHJy0BPl_udcbqac39WF1cllsEV3l------------",
"notification": {
"title": "10-inch Aggretsuko Rage #24 (Jumbo Size)",
"subtitle": "Test notification Krunal"
}
}
But when I used the same payload format that mentions in the apple documentation then the notification is sent successfully but not received on the device. Check this payload,
{
"to": "cMXdSK_ud0LpqMoju85FvT:APA91bFMke92QR1IvcLeLhG5XrvwcE0OfSLGpJW1ds9-FFDornoeorMlKOn6IEtDYsuvwlRrwJnHJy0BPl_udcbqac39WF1cllsEV3l------------",
"aps" : {
"alert" : {
"title" : "10-inch Aggretsuko Rage #24 (Jumbo Size)",
"subtitle" : "Test notification Krunal",
},
"category" : "GAME_INVITATION"
},
"gameID" : "12345678"
}
Can someone please explain what's the actual problem with the above payload I want to use that in my flutter app. When I used the notification keyword in the payload that create a problem in the flutter android app. Can someone please help me to solve this problem?
What is happening here is that you're using FCM messages. When you do this, you actually send the notification payload to Firebase. Firebase then transforms this payload into the appropriate payload format for iOS or Android or web and then sends it to the respective messaging service.
For the payload that you've built looking at the Apple documentation to work you would have to send it to APNS.
You can specify Apple specific keys in your FCM message as-well. See the documentation here.
You can use the ApnsConfig object to specify things that are specific to Apple devices.
To set options specific to Android devices you may use the AndroidConfig object.
Now, coming to the problem you mention with the Android app, looking at the 'to' field in your payload, I must assume you are using one of the legacy APIs (XMPP Server Protocol or Legacy HTTP Server Protocol) instead of the ones I have suggested above. In this case, are you sure you have updated the recipient in the 'to' field when sending the payload? If yes, then could you please explain the problem you are seeing in your Android app when using the FCM specific payload.
I think the problem is that Firebase wants a different JSON structure than the proprietary data structure that actually ends up being used on Apple systems. Firebase is doing the conversion for you from your first example.
Your second example is the kind of payload the Apple Simulator can make use of xcrun simctl push so it is still helpful for local testing and development.
The final part of your question about why it is a problem for Android I am not sure about. But I wonder whether you supplied the wrong FCM token because that might encode the intended platform type within in making it incompatible for Android delivery. Firebase seems to always know the right platform specific way to deliver a push message, whether it is Android or iOS, so it must keep a track of that via the FCM token.
This bounty has ended. Answers to this question are eligible for a +500 reputation bounty. Bounty grace period ends in 5 hours.
Scorb wants to draw more attention to this question.
I have a cross platform flutter app. I am using FCM to send notifications and they are not arriving on the iOS app in any state (foreground, background, terminated). I am testing on a physical device.
The notifications work on Android, so I know the topic subscription and send code is correct.
I have followed these steps for configuration defined here...
https://firebase.flutter.dev/docs/messaging/apple-integration/
To verify I have:
added and initialized firebase using google-services-info.plist.
created and added my APNS key
created the app identifier in the Apple Developer center and confirmed it matches my apps bundle id (and added push notification
capability for the identifier.
added push notification capability to the app in xcode
added background fetch and background remote notification capabilities to the app in xcode.
It seems like I have addressed every step to configure FCM on iOS. But none of my notifications arrive.
When the app is minimized, no system tray notification is triggered, and when in the foreground the FirebaseMessaging.onMessage is never triggered, like is is when on Android.
An important note is that this is the second iOS app added to this project, and the first iOS app works correctly. Though there is no indication that there are any special steps for a second app.
We saw a similar problem (for a native iOS app) where FCM would deliver to one app in the project but not the other. The mistake we made was not repeating the configuration in Firebase for the other apple bundle id since each app in our project has a different bundle id. Given it works for one of your iOS apps but not the other on the same project, it sounds like you've got the same issue.
Also, a good diagnostic step is to use the Firebase Console to manually send off a Push Notification to a particular app. It means you can then discount errors further upstream in your backend that may not have caused FCM to be invoked.
Have you tried to send testing notifications via firebase console?
You can log your device token in your app.
Background notification must work if you have done correct with firebase and APN settings despite without implementing some methods of AppDelegate file for remote notification on the next step.
Your question is very blur to have a specific answer but you can separate your concerns to find the issue and then try to resolve that:
1. Make sure about the APNs configuration
First, try to send a notification to your iOS app, directly using the APNS key. If you get the notification, it means you have configured the APNS correctly
2. Make sure about the firebase setup
Check with both the Android and iOS app and see if there are connected to the firebase console. You can use the real-time event to confirm that.
3. Make sure you have implemented the correct methods.
Firebase and apple have different methods for push notifications. Make sure you are implementing the correct one. Also, it may vary based on the configuration file of firebase. search for iOS firebase swizzling for more information
4. Make sure you got the right permissions from the user
You should get the notification permission from the user before trying to show any notification. Make sure you've done that
Note that you may have already tried some of these but I've mentioned them for the sake of completeness. Hope it helps you and others.
I am working on getting Firebase Cloud Messaging to work with iOS via Flutter. I have followed the steps laid out here, here, and and here with no luck.
I am NOT using simulator, I am on an iPhone 8+ with iOS 11.4.1. I have installed all three APN certs in Firebase console. I have called FirebaseMessaging.requestNotificationPermissions(); and accepted the dialog. I am testing by sending messages through FCM console. I have my phone auth'd with Firebase (anonymous auth).
I do not receive messages with the app open or closed.
If anyone has any thoughts on what I might be missing, please assist. I would like to be able to make a bulletpoint list for others coming to Flutter/iOS/FCM to just follow without errors.
Alright, this is what I learned. Wish I had written this all down right when I got it working. But it should be helpful to someone.
Make sure Firebase is setup and working in your Flutter project.
Add firebase_messaging to pubspec.yaml
flutter packages get
Create/download your APNS key and upload it to Firebase console
Create/download your Provisioning Profile on Apple Dev website and double-click to install.
Use the important pieces of the snippet below
Send a message to all app users, or your messagingToken through Firebase console.
If you have a physical device running your app and you follow these steps, you should receive background notifications. You will not receive them in the foreground. If someone figures out how to get them in the foreground, let me know!
snippet
import 'package:firebase_messaging/firebase_messaging.dart';
FirebaseMessaging messaging = FirebaseMessaging();
messaging.configure(); // NECESSARY
messagingToken = await fb.messaging.getToken();
messaging.subscribeToTopic("general");
// this will launch a modal asking if you want to receive notifications
messaging.requestNotificationPermissions();
Sounds like you are missing some configuration steps in order to be able to send push notifications to your iOS App. Maybe the best that you can do is post more information about your configuration environment.
However, for the description that you give us, it can be an issue about one of the following options:
You need to configure the correct environment to send the push notification. If you install the app to the device directly from Xcode you need to use the Sandbox environment, but if your app is installed from AppStore or Testflight, you need to use Production. This is because both environment (sandbox and production) refers to different urls to send the push notification.
The deviceId related to the specific relation between your app and the current device is not stored. Remember, when you send a push notification you need to give which devices will receive that notification.
Please let me know if this responds your question or there is some that I've missing
EDIT
To handle foreground notifications you need to add the didReceiveRemoteNotification callback in order to get the title, message all the custom parameters of the JSON structure.
In this particular case, the plugin documentation says that you need three different callbacks, depending on the application status.
If the app is in FOREGROUND you need to use onMessage callback
If the app is in BACKGROUND you need to use onResume callback
If the app is TERMINATED you need to use onLaunch callback
However, this only make the parameters info available, you still needs to show them to the user in some custom way (for example WhatsApp or Facebook Messenger can show you the new chat message if you are in a different conversation as a independent bubble on top of the view, or this new message is added to the bottom of the conversation if it belongs to the current chat).
Is it possible to send data messages to iOS devices in FCM without using APNs?
I need to send some "live" data to my iOS client app while it is running, instead of polling my servers.
There are 2 reasons I want FCM not to use Apple notifications:
I don't want any kind of system notification/badge/alert to be shown to the user when the app is not running. This is pure app data transfer while the app is running, the rest of the messages can be ignored.
When using Apple notifications the user will be asked the first time if (s)he wants to grant my app notification permissions. If denied my whole app will stop working since the data will not be updated.
I found this post at Firebase Google Groups here where some of the Firebase guys hints that if I don't use iOS specific keys like sound, body, badge, content_available or others:
{
"to" : "<device_id>",
"data" : {
"customKey1" : "value1",
"customKey2" : "value2",
"customKey3" : "value3",
}
}
then they will not use APNs.
All I need is a guarantee of a delivery while my app is in foreground, I wonder if there is a way to configure FCM or messages themselves to reach that goal.
Just a follow up on this. I'm finding that with React Native using this library https://rnfirebase.io/docs/v5.x.x/messaging/receiving-messages i am able to receive data only messages to the iOS simulator (which doesnt support APNS) so looks like data only bypasses APNS.
If i have a pass generated and distributed over a bunch of PassBook users' devices, and i want to send a push notification outside of a pass update, would i be able to do so?
The documentation isn't clear. Has anyone had any experience with this. https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/PassKit_PG/Chapters/Updating.html#//apple_ref/doc/uid/TP40012195-CH5-SW59 I understand i'll have all the pushTokens from the devices usin fmy pass, so could i use them to send out notifications?
Once i build the web service that pushes the updated passes, can i use that service to send notifications without pass update? Just random communication with users?
You need to change the past content and have a changeMessage key set for the updated field(s) if you want anything to show on the lockscreen.
The following answer details all of the mandatory requirements to invoke a lock screen message from a push to a pass. How to make a push notification for a pass.
Specifically note point 5:
alert, badge, sound and custom property keys are all ignored - the
push's only purpose is to notify Passbook that your web service has a
fresh pass. The notification text will be determined by the
changeMessage key in pass.json and the differences between the old and
the new .pkpass bundles