parse-community / parse-server

Parse Server for Node.js / Express
https://parseplatform.org
Apache License 2.0
20.85k stars 4.78k forks source link

Parse.Push not silent with push_type="background" on ios #6369

Open taivo opened 4 years ago

taivo commented 4 years ago

Issue Description

The issue I encountered can be replicated by using the Push functionality of Parse Dashboard. I'm using parse-server 3.9 on Back4App. The behavior I'm looking for is a silent push that does not trigger a notification on iOS when the app is in the background (or not running), but still receive the payload content to do my own processing when the app is running in the foreground.

Steps to reproduce

Use the Parse Dashboard's json push functionality with the following payload:

{
"title":"2",
"content-available":1,
"push_type":"background",
"priority":5,
"custom": {"field1": "foo"}
}

When monitoring my phone via Xcode, I can see the following payload being received:

{"subtitle":"","title":"2","body":"","badge":1,"data":{"custom":{"field1":"foo"},"aps":{"alert":{"title":"2"},"content-available":1},"push_type":"background","priority":5},"id":"-------------"}

However, because of the "alert" field in the aps dictionary, this push will not be silent when the app is in the background or not running. If we remove the "title" field in the Json payload from Parse Dashboard, the notification does not reach my phone/Xcode, so i'm not able to do custom processing on it while the app is running.

Note that in the received payload, the "push_type" field is outside of the "aps" dictionary instead of inside it. I'm suspecting this may be the issue but currently do not have the right setup to perform a trace/debug.

Is there a way to work around this issue? Right now I'm settling for using a badge updates but true silent notification would be much better. I'm using Back4app and have tried both parse-server 3.9.0 and 3.10 beta, which is the latest available there.

TomWFox commented 4 years ago

@funkenstrahlen would you be able to take a look into this?

also perhaps this should be moved to the push adapter repo?

funkenstrahlen commented 4 years ago

The app payload must not contain the alert key for the notification to be delivered in background.

To send a background notification, create a remote notification with an aps dictionary that includes only the content-available key, as shown in Listing 1. You may include custom keys in the payload, but the aps dictionary must not contain any keys that would trigger user interactions.

(from Apple Documentation)

However I think you already know that and only added it for testing.

The push-type is handled differently. It is send in the header of the request to APNS. This is done in node-apn here: https://github.com/parse-community/node-apn/blob/a67c5cdd45ba5e3cbfc9b59d51feed98ef50067d/lib/notification/index.js#L61-L63

You are correct push_type is included in the data property, because it does not get discarded by node-apn when delivering the notification and setting the header. However it is just as any other custom field and therefore should not impact background delivery.

Why does it not work for you? You need to make sure content-available is inside the aps dictionary. I do not know right now how to do that in Parse Dashboard. However this is clearly the issue here.

In my cloud code it looks like this for a silent notification:

return Parse.Push.send({
      channels: [ channel ],
      data: {
        "content-available": 1,
        push_type: "background",
        payload: payload
      }
    }

Everything inside data then gets handled here to build the actual notification for node-apn: https://github.com/parse-community/parse-server-push-adapter/blob/c9a1fc65bb61c71adaf4c79df18420c2c6546849/src/APNS.js

taivo commented 4 years ago

Strange, that's exactly what I have in my code and prompted me to use the dashboard to create an easily replicable example. Here's how it looks in my code, which seems the same as yours.

Parse.Push.send({
    channels: [channel],
    data: {
        //badge: "Increment",
        "content-available": 1,
        push_type: "background",
        priority: 5,
        payload: payloadObj
    }
}, { useMasterKey: true });

If I don't enable the "badge" field (or "alert" or "title"), the silent notification just does not arrive to my app running in the foreground.

When I enable badge, alert, or title, I see the aps dictionary containing "content-available", but not "push_type". "push_type" is placed outside of aps.

I'm using iOS 13.3.1. In google searches for silent notification on iOS, there seems to be a similar issue for various other projects where silent push seems to stop working on iOS 13. They all mention something regarding the "apns-push-type" header field. I wonder if this may be related. Example here https://github.com/rpush/rpush/issues/534

funkenstrahlen commented 4 years ago

Interesting. If you want to make sure the push type is not the cause of the issue you can omit it for notifications to iOS. The Apple documentation states that is is only required for watchOS. On iOS it’s just recommended. So it should work without it. However I can not guarantee the Apple documentation is up to date in this case.

We should also check if this is a problem caused by the notification priority. Because a priority lower than 10 does allow the APN to not deliver the notification at all or delay them.

funkenstrahlen commented 4 years ago

@taivo Can you please try with a priority of at least 10?

funkenstrahlen commented 4 years ago

I know iOS 13 requires the new header key when sending push notifications to APNS. This functionality has been added to node-apn a long time ago exactly because of this change on Apples side.

Parse Server uses the parse-push-adapter and the push adapter uses node-apn to send notifications.

You can find the code setting the header key here: https://github.com/parse-community/parse-server-push-adapter/blob/c9a1fc65bb61c71adaf4c79df18420c2c6546849/src/APNS.js#L101

https://github.com/parse-community/node-apn/blob/a67c5cdd45ba5e3cbfc9b59d51feed98ef50067d/lib/notification/index.js#L61-L63

I do not know why silent notifications are not sent correctly. However I am sure Parse has already implemented support for the new header key required on iOS 13.

dplewis commented 4 years ago

I believe we already addressed this. Duplicate https://github.com/parse-community/parse-server/issues/6106

The answer should be in there.

sworteu commented 3 years ago

It does (should) work but the documentation is wrong. This is as far as we've got it for the json: root object keys: push_type - "alert" or "background" content-available - 1 if this is a push_type "background" channels - string array of channels or not set for everyone/all channels

data: { object } - Keys below are inside the data object: alert - string message to send if push_type "alert" priority - 5 for "alert" or "background" (required when 'background') or 10 custom - a custom json object. (or should this key be "payload" ?)

example json background iOS push:

{
  "push_type": "background", 
  "content-available": 1, 
  "data": {
    "priority": 5,
    "category": "testing",
    "payload": {"custom-key": "custom-value"}
}}

Perhaps someone will update the parse documentation as it is crucial that this is clear how it should be used, currently it shows that everything should be inside the "data" object but that's not true. Some keys won't be used if those are inside the data object and should be inside the json root as seen above.

As seen here: https://docs.parseplatform.org/rest/guide/#sending-pushes The docs show that everything is inside the "data" object. No information is given about outside of the "data" object (e.g. inside the root object).