Firebase silent apns notification
B

7

50

Is there a way to send a silent APNS using google's firebase? It seems that if the app is in the background it will always show a notification to the user.

Thanks?

Bayberry answered 1/6, 2016 at 13:30 Comment(2)
By using googles firebase, do you mean the Firebase Console?Hertha
I mean the api. I want to send a silent notification through our JAVA server, not through the Console. And as far as I see you can't send a notification that will wake you application and not automatically put a visible notification in the tray.Bayberry
P
55

You can send silent APNS messages using the FCM server API https://firebase.google.com/docs/cloud-messaging/http-server-ref

In particular you need to use:

  • The data field:

This parameter specifies the custom key-value pairs of the message's payload.

For example, with data:{"score":"3x1"}:

On iOS, if the message is sent via APNS, it represents the custom data fields. If it is sent via FCM connection server, it would be represented as key value dictionary in AppDelegate application:didReceiveRemoteNotification:.

The key should not be a reserved word ("from" or any word starting with "google" or "gcm"). Do not use any of the words defined in this table (such as collapse_key).

Values in string types are recommended. You have to convert values in objects or other non-string data types (e.g., integers or booleans) to string

  • The content_available field:

On iOS, use this field to represent content-available in the APNS payload. When a notification or message is sent and this is set to true, an inactive client app is awoken. On Android, data messages wake the app by default. On Chrome, currently not supported.

Full documentation: https://firebase.google.com/docs/cloud-messaging/http-server-ref#downstream-http-messages-json

Poon answered 3/6, 2016 at 18:33 Comment(2)
hi , i have a similar scenario but slightly modification required , like when i get the push notification ,the app should get active in background and do the task given in , ( like user's location update code ) ,- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo , but my app would not get active until user taps on the notification . so , how would i get this done while app is in background ? dose anyone know the solution for this ?Lineage
@Lineage Even I am struggling with the same issue. Were you able to solve this?Padang
K
48

For a truly silent notification (both foreground and background) using FCM server use these fields:

"to" : "[token]",
"content_available": true,
"priority": "high",
"data" : {
  "key1" : "abc",
  "key2" : 123
}

NOTE: Make sure you are using "content_available" NOT "content-available" with FCM. It's converted for APNS and won't be received properly otherwise. The difference had tripped me up for some time.

UPDATE: Hey all, please note that this answer was for an earlier version of FCM and the answer here is outdated. Please reference the updated answer below (https://mcmap.net/q/347101/-firebase-silent-apns-notification) as well as Google's migration docs: https://firebase.google.com/docs/cloud-messaging/migrate-v1

Kit answered 3/4, 2017 at 14:27 Comment(5)
Thanks! "content_available" was just what I needed!Honeysuckle
How to receive this payload on iOS, I've achieved it on Android but just not getting how to do it in iOS. Could you please help me? Thank youKingsize
Error: Invalid JSON payload received. Unknown name "content_available"Aurignacian
Life Saver my guy!!Dimidiate
Could we see a more full example here? I'm also seeing Unknown name "content_available" and I don't know how to get this to work without error after error...Gileadite
L
18

I explain this topic more detail on my blog. http://blog.boxstory.com/2017/01/how-to-send-silent-push-notification-in.html

** keypoint is : "content_available:true"

this is sample JSON

{
    "to" : "<device>",
    "priority": "normal",
    "content_available": true, <-- this key is converted to 'content-available:1'
    "notification" : {
      "body" : "noti body",
      "title" : "noti title",
      "link": "noti link "
    }
}

Note: If the above sample JSON is sent, then the notification will be visible to the user. Use below If you don't want the user to see the push notification.

{
  "to": "<device>",
  "priority": "normal",
  "content_available": true <-- this key is converted to 'content-available:1'
}
Lebna answered 8/1, 2017 at 9:25 Comment(3)
A link to a potential solution is always welcome, but please add context around the link so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline. Take into account that being barely more than a link to an external site is a possible reason as to Why and how are some answers deleted?.Elaterin
@spacitron fcm is firebase cloud messaging, so he does ;)Elihu
do not send body , title , link , inside the notification otherwise user will get visible notifications . if you want to send totally silent notification then add some custom parameter in notification or keep it blank . you can refer this link , #47770756 .Lineage
K
15

For guys who do not use the Legacy HTTP as is shown in other answers and use the latest v1 HTTP protocol, I've finally figured out the right way to send silent notifications.

NodeJS example using firebase-admin:

    const message = {
      apns: {
        payload: {
          aps: {
            "content-available": 1,
            alert: ""
          }
        }
      },
      token: "[token here - note that you can also replace the token field with `topic` or `condition` depending on your targeting]"
    };

    admin
      .messaging()
      .send(message)
      .then(response => {
        // Response is a message ID string.
        console.log("Successfully sent message:", response);
      })
      .catch(error => {
        console.log("Error sending message:", error);
      });

Explanation:

  • It seems the payload in apns does not get converted by Firebase in v1 HTTP protocol so you need the original "content-available": 1 for that.
  • alert: "" is also necessary. If you ever try to send silent notifications using something like Pusher, you will find only content-available cannot trigger it. Instead, adding additional field such as sound or alert can make it work. See Silent Push Notification in iOS 7 does not work. Since Firebase forbids empty sound, we can use empty alert for this.
Knuckle answered 16/5, 2019 at 9:47 Comment(1)
I actually wasn't able to get things working without specifying a value for sound as well as alert. It seems like you can just put in a random value and it will work. sound: "something.wav"Pyxie
O
5

The other solutions did not work for me. I wanted a solution that sent data messages to both iOS and Android.

From my testing I have found that the only way to reliably send a data message when my iOS app is in the background is to include an empty notification payload.

Also, as other answers have mentioned, you need to include content_available and priority.

To test this out using a curl command you'll need your FCM server key and the FCM token from the app.

Sample curl command for iOS only. (Reliable data message with no visible notification)

curl -X POST \
  https://fcm.googleapis.com/fcm/send \
  -H 'authorization: key=server_key_here' \
  -H 'content-type: application/json' \
  -d '{
  "to": "fcm_token_here", 
  "priority": "high",
  "content_available": true,
  "notification": {
    "empty": "body"
  },
  "data": {
    "key1": "this_is_a_test",
    "key2": "abc",
    "key3": "123456",
  }
}'

Replace server_key_here and fcm_token_here above with your own.

The below method in your AppDelegate class should be called when the app is in the background and no UI message should be displayed.

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    //get data from userInfo

    completionHandler(UIBackgroundFetchResult.newData)
}

Here's how you would do this using a cloud function and typescript to send to a topic.

const payload = {
    notification: {
        empty: "message"
    },
    data: {
        key1: "some_value",
        key2: "another_value",
        key3: "one_more"
    }
}

const options = {
    priority: "high",
    contentAvailable: true //wakes up iOS
}

return admin.messaging().sendToTopic("my_topic", payload, options)
    .then(response => {
        console.log(`Log some stuff`)
    })
    .catch(error => {
        console.log(error);
    });

The above seems to consistently work for iOS and sometimes work for Android. I've come to the conclusion that my backend will need to determine platform before sending out a push notification to be most effective.

Ootid answered 15/8, 2020 at 20:55 Comment(1)
Thanks for the sample URL command! Saved me a lot of time testing messaging issues!Dilatation
G
1

I needed to send scheduled notifications to topics. Nothing above quite worked for me, but I finally got the app delegate application(application:didReceiveRemoteNotification:fetchCompletionHandler:) called consistently. Here's a complete example of what I ended up doing in my index.js cloud function file (note that you need "apns-push-type": "background" and "apns-priority": "5" headers, as well as the "content-available": 1 entry inside the aps object):

const admin = require("firebase-admin");
const functions = require("firebase-functions");

exports.sendBackgroundFetchNotification = functions.pubsub.schedule("every 1 hours").onRun((context) => {
  const message = {
    data: {},
    apns: {
      headers: {
        "apns-push-type": "background",
        "apns-priority": "5",
      },
      payload: {
        aps: {
          "content-available": 1,
          "alert": {},
        },
      },
    },
    topic: "[Topic_Name_Here]",
  };

  return admin
    .messaging()
    .send(message)
    .then(response => {
      // Response is a message ID string.
      console.log("Successfully sent message:", response);
      return null;
    })
    .catch(error => {
      console.log("Error sending message:", error);
      return null;
    });
});

If you don't want to wait around for the function to trigger once you deployed it, just go into the Google cloud console functions section (https://console.cloud.google.com/functions/list) and click on the function name, then click "Testing" and finally click "TEST THE FUNCTION".

It's worth noting that this code uses FCM's newer HTTP v1 Protocol which allows you to build a message object based on Apple's specs (helpful links below).

https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification

Gileadite answered 4/1, 2022 at 22:3 Comment(0)
A
0

i had the same issue. when app was in foreground, it received all data messages, but when it was in background, it only showed notification, and received data only when user tap on it.

the key point to start receiving data messages in background was the "background processing" mark in xcode: sceenshot

it works with both versions of firebase api, even without "notification" section in message. If you work with legacy (https://fcm.googleapis.com/fcm/send), you need to add "content_available":true flag to push json. in new api you can set just token and data:

{
"message": {
    "token": "YOUR_FIREBASE_DEVICE_TOKEN",
    "data": {
        "KEY": "VALUE"
    }
}

}

And with these settings, both of android and ios devices work with same behaviour

Abaft answered 21/6 at 12:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.