3

I am trying to send critical alerts from PHP backend to my iOS app via FCM. The problem is, that the notifications are not treated as critical alerts and won´t bypass "Do Not Disturb"-mode.

Payload sent from Backend to FCM (Example)

{
    "to": "<fcm token>",
    "data": {"property-1": "value-1"},
    "priority": "high",
    "notification": {
        "title": "Hello World!",
        "body": "Lorem Ipsum ...",
        "badge": 1
        "sound": {
            "critical": 1,
            "name": "default",
            "volume": 1.0
        },
        "color": "#ffffff"
    },"time_to_live": 300,
    "content_available": true
}

According to the Apple documentation this format should be valid.

In settings critical alerts are enabled.

The only suspicious thing I´ve found so far, is that the userInfo variable holding the information of the push notification holds sound as JSON-string instead of a serialized object.

Printing description of userInfo:
▿ 4 elements
  ▿ 0 : 2 elements
    ▿ key : AnyHashable("gcm.message_id")
      - value : "gcm.message_id"
    - value : <message-id>
  ▿ 1 : 2 elements
    ▿ key : AnyHashable("google.c.a.e")
      - value : "google.c.a.e"
    - value : 1
  ▿ 2 : 2 elements
    ▿ key : AnyHashable("aps")
      - value : "aps"
    ▿ value : 4 elements
      ▿ 0 : 2 elements
        - key : content-available
        - value : 1
      ▿ 1 : 2 elements
        - key : alert
        ▿ value : 2 elements
          ▿ 0 : 2 elements
            - key : title
            - value : Hello World!
          ▿ 1 : 2 elements
            - key : body
            - value : Lorem Ipsum ...
      ▿ 2 : 2 elements
        - key : badge
        - value : 1
      ▿ 3 : 2 elements
        - key : sound
        - value : {"volume":1.0,"critical":1,"name":"default"}
  ▿ 3 : 2 elements
    ▿ key : AnyHashable("message")
      - value : "message"
    - value : {"data":...}

Is this a bug in iOS or APNS?

{"volume":1.0,"critical":1,"name":"default"}

Any ideas how to get this working?

orange01
  • 1,584
  • 1
  • 16
  • 28
  • 3
    Does your app have a critical alert entitlement? https://medium.com/@shashidharyamsani/implementing-ios-critical-alerts-7d82b4bb5026 You can't just decide to send critical alerts. Apple has to agree that your app is worthy of bypassing a user setting and then the use has to approve it as well. – Paulw11 Nov 21 '19 at 20:22
  • Yes, I got the entitlement and the right provisioning profile. – orange01 Nov 21 '19 at 20:24
  • I don't really have an answer for you, but I know from a colleague that was straggling with silent push not working that he was able to solve this by `changing the order of the headers` of the notification. He was using AWS SNS. – Gal Dec 01 '19 at 09:37
  • ummm. I'm confused. isn't `{"volume":1.0,"critical":1,"name":"default"}` a dictionary?! – mfaani Dec 02 '19 at 17:10
  • I know you've enabled critical alerts, but try doing a restart after that. – mfaani Dec 02 '19 at 17:21

2 Answers2

2

The only suspicious thing I´ve found so far, is that the userInfo variable holding the information of the push notification holds sound as JSON-string instead of a serialized object.

That sounds indeed like the problem you could be facing. According to the Apple Documentation, the sound property has to be a dictionary for critical alerts (Apple Developer – Generating a Remote Notification):

A dictionary that contains sound information for critical alerts. For regular notifications, use the sound string instead.

I recommend you test sending out a push notification for testing purposes with a tool like this one GitHub – onmyway133/PushNotifications where you specify the sound property to be a dictionary and see what happens.

Maybe this is a bug in FCM? If the test shows, that the push notification works as intended, this could be the case.

ChaosCoder
  • 3,085
  • 1
  • 22
  • 23
  • This testing tool unfortunately didn´t work, but I tried an online FCM testing tool and received the notification on iOS, but it was no critical alert. – orange01 Dec 03 '19 at 07:47
2

Critical alerts

Firstly, some information relating to Apple Push Notification Service.

From Apple docs: criticalAlertSetting:

When UNNotificationSetting.enabled, this property authorizes the app to play critical sounds that ignore Do Not Disturb and the device’s mute switch.

For remote notifications, the system attempts to play a critical sound when the notification’s payload contains a sound directory that contains the critical key. Critical alerts require a special entitlement issued by Apple.

From Apple docs: UNNotificationContent:

Don't create instances of this class directly. For remote notifications, the contents of this object are derived from the JSON payload that your server sends to the APNS server.

UNNotificationContent has several properties:

var title: String // A short description of the reason for the alert.
var subtitle: String // A secondary description of the reason for the alert.
var body: String // The message displayed in the notification alert.
var badge: NSNumber? // The number to display as the app’s icon badge.
var sound: UNNotificationSound? // The sound to play when the notification is delivered.
var launchImageName: String // The name of the launch image to display when your app is launched in response to the notification
var userInfo: [AnyHashable : Any] // A dictionary of custom information associated with the notification.
var attachments: [UNNotificationAttachment] // An array of attachments to display with the notification.

Notice that the sound property and the userInfo property are separate. This means that the critical alert setting must be stored under sound and not under userInfo.


Firebase Cloud Messaging (FCM)

Previously, Firebase Cloud Messaging (FCM) did not support critical alerts and it had to be done directly with Apple Push Notification Service (APNS). However, I understand the FCM API now has a sound property for this.


Two important points:

1: You must have a critical alerts entitlement from Apple for this to work. You have to justify why your app should be allowed to circumvent user preferences to provide a notification even when Do Not Disturb is switched on. You must then update your provisioning profile with this in Xcode.

2: You must also request permission specifically for critical alerts when requesting permission for notifications:

var authOptions: UNAuthorizationOptions?
if #available(iOS 12.0, *) {
    authOptions = [.alert, .badge, .sound, **.criticalAlert**]
} else {
    authOptions = [.alert, .badge, .sound]
}
UNUserNotificationCenter.current().requestAuthorization(options:   
  authOptions!) { (granted, error) in
    // Handle outcome here
}

The user will then be prompted to allow critical notifications specifically.


In summary, this should be possible with the newer version of FCM, but will definitely work using native APNS, provided the steps above are implemented.

You might also want to check this answer for a way to use a notification extension to make the alert critical when it is received. The mutable-content key must be set to true, so that the notification extension is invoked.

Community
  • 1
  • 1
Chris
  • 4,009
  • 3
  • 21
  • 52
  • 1
    Thanks for the great feedback! I´ll try the new FCM HTTP v1 API if it works. I also got the entitlement from Apple and the development provisioning profile with critical alerts enabled. – orange01 Dec 03 '19 at 07:44
  • 1
    I tried the new FCM HTTP v1 API in combination with kreait/firebase-php and it is working like charm now. Thanks! – orange01 Dec 27 '19 at 13:32