Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[🐛] iOS not receiving silent background push notifications via Firebase #4697

Closed
trbsi opened this issue Dec 21, 2020 · 19 comments
Closed
Labels
help: needs-triage Issue needs additional investigation/triaging. type: bug New bug report

Comments

@trbsi
Copy link

trbsi commented Dec 21, 2020

I went through every issue reported regarding this problem but I couldn't find a solution.
The same code works on Android, but not on iOS. I'm trying to receive silent background push notification so I can modify it via Notifee.

Information:
I followed the whole tutorial from https://rnfirebase.io/messaging/usage
iOS 14.2
Foreground working fine

I'm sending notification manually and testing when my app is in background. I'm getting notification when I include "notification": {"title": "soemthing"}.

curl -H "Content-type: application/json" -H "Authorization:key=MY_KEY"  -X POST -d '{"content_available": true, "data": { "foo": "1","bar": "2"},"to" : "MY_TOKEN", "apns":{"payload": {"aps": {"content-available" : 1}}, "headers":{  "apns-priority":"5",  "apns-push-type":"background", "apns-topic": "MY_BUNDLE_ID"}}}' https://fcm.googleapis.com/fcm/send

I can confirm that notifications are working.

Here is my code:
packages.json

"dependencies": {
    "@notifee/react-native": "^0.15.1",
    "@react-native-async-storage/async-storage": "^1.13.2",
    "@react-native-community/masked-view": "^0.1.10",
    "@react-native-community/netinfo": "^5.9.9",
    "@react-native-firebase/app": "^10.2.0",
    "@react-native-firebase/messaging": "^10.2.0",
    "@react-native-picker/picker": "^1.9.3",
    "@react-navigation/bottom-tabs": "^5.11.2",
    "@react-navigation/native": "^5.8.10",
    "@react-navigation/stack": "^5.12.8",
    "expo": "~39.0.2",
    "expo-splash-screen": "~0.6.2",
    "expo-status-bar": "~1.0.2",
    "expo-updates": "~0.3.2",
    "moment": "^2.22.2",
    "react": "16.13.1",
    "react-native": "~0.63.3",
    "react-native-button": "^2.3.0",
    "react-native-contacts": "^6.0.3",
    "react-native-device-info": "^7.3.1",
    "react-native-gesture-handler": "^1.7.0",
    "react-native-image-picker": "^3.0.1",
    "react-native-keyboard-manager": "^4.0.13-17",
    "react-native-permissions": "^3.0.0",
    "react-native-safe-area-context": "^3.1.9",
    "react-native-screens": "^2.10.1",
    "react-native-toast-message": "^1.4.1",
    "react-native-unimodules": "~0.11.0",
    "react-native-vector-icons": "^5.0.0"
  },

index.json

messaging().setBackgroundMessageHandler(async message => {
    console.log('message background: ');
    console.log(message);
    logToServer('Ušao u setBackgroundMessageHandler.onMessageReceived');
    logToServer(message);

    const accessToken = await AsyncStorage.getItem('@passwordAccessToken');
    if (null === Firebase.notificationProvider) {
        logToServer('Firebase.notificationProvider je null');
        if (Firebase.PUSH_PROVIDER === Firebase.PUSH_PROVIDER_NOTIFEE) {
            Firebase.notificationProvider = new NotifeePushProvider();
            logToServer('Kreirao je new NotifeePushProvider');
        }
    }


    //when user logs out he shouldn't receive push, with "onMessage()" method I can unsubscribe
    //with setBackgroundMessageHandler() I cannot unsubscribe so I need to make this check
    if (null !== accessToken) {
        Firebase.notificationProvider.displayNotification(message.data);
        logToServer('Pozvao se displayNotification');
    }
});

function HeadlessCheck({ isHeadless }) {
    if (isHeadless) {
        // App has been launched in the background by iOS, ignore
        return null;
    }

    return <App />;
}

// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in the Expo client or in a native build,
// the environment is set up appropriately
registerRootComponent(HeadlessCheck);

I even tried with other functions but they are for foreground:


async function onMessageReceived(message) {
    console.log('onMessage: ');
    console.log(message);

    const accessToken = await AsyncStorage.getItem('@passwordAccessToken');
    if (null === accessToken) {
        return;
    }

    Firebase.notificationProvider.displayNotification(message.data);
}

async function onMessageReceivedNotificationOpenedApp(message) {
    logToServer('Usao je u onMessageReceivedNotificationOpenedApp');
    onMessageReceived(message);
}

function subscribeToPushNotifications() {
    messaging().onNotificationOpenedApp(onMessageReceivedNotificationOpenedApp);
    messaging().onMessage(onMessageReceived);
}

It never enters setBackgroundMessageHandler, it enters when I build on Android. As you may see I added logToServer and I cannot see any logs from iOS on my server.

I exhausted all of options, I don't know what's going on.

@trbsi trbsi added help: needs-triage Issue needs additional investigation/triaging. type: bug New bug report labels Dec 21, 2020
@mikehardy
Copy link
Collaborator

I'm not sure about that FCM JSON, why is content-available in there twice 🤔 - makes the rest seem suspect. I would triple check that.

More importantly, it's important to know that data-only FCM to iOS is not reliable. It works much of the time but there is never a guarantee it will work. If you went through all the other issues you would see advice to launch release mode build on real device from Xcode so you could watch the console output. If you do that then you may notice as mentioned in other issues that the device receives the FCM but just does not decide to launch the app for whatever reason (throttling for CPU usage, temparature, because low power mode is on, etc etc). There is never a guarantee.

For 100% guarantee you must use an actual notification payload (or mixed payload) and have the SDK display a notification itself. That is irritating because it doesn't give you the device API control over the notification that Notifee would, but it is the way iOS and the native firebase-ios-sdk is designed unfortunately.

I believe you still need to ask for permission as well, I don't see that referenced anywhere.

@trbsi
Copy link
Author

trbsi commented Dec 21, 2020

I tried various combinations for payload. I'm wondering why not even one silent notification is received. I have permission for receiving push, I checked that.

Would non-FCM data-only push notifications be more reliable?

@mikehardy
Copy link
Collaborator

data-only push notifications are not reliable. I don't believe sender has anything to do with it as they all go to APNS

you tried various combinations of payload but settled on that is objectively invalid? I don't understand that

Here is a flag for more debugging in Xcode console #4603 (comment)

Here is the reference https://firebase.google.com/docs/cloud-messaging/http-server-ref

@trbsi
Copy link
Author

trbsi commented Dec 21, 2020

Not settled, it really doesn't matter which payload I send it's always the same, not working. I'll close the issue, maybe I'll figure something out, if I do I'll post my solution here

@trbsi trbsi closed this as completed Dec 21, 2020
@mikehardy
Copy link
Collaborator

I will be really curious for any final solution that works, or a root cause (if you see one with debug flag enabled and Xcode console watching...)

@trbsi
Copy link
Author

trbsi commented Dec 22, 2020

I tried with https://github.com/react-native-push-notification-ios/push-notification-ios
The same problem remains.

I tried with sockets, I thought that sockets will be connected all the time so I'll be able to trigger local push notifications via sockets, once app goes to background socket connection breaks.

There is no way to be 100% sure that push will be delivered which is important for me. I trashed my app.

@mikehardy
Copy link
Collaborator

Network connections are unreliable by definition, and especially so on mobile of course
Notifications are notoriously abused by developers to spam users or wake up apps too much, so they are tightly controlled

There is just no avoiding that, but the more a user uses your app and the more value they receive from it, the more likely they are to allow notifications and the more the power miser algorithm will grant the app wakeups etc., like whatsapp etc

@trbsi
Copy link
Author

trbsi commented Dec 23, 2020

I even try to send payload with "notification" key, such as

{
    "content_available": true,
    "data": {
        "sender_id": "2",
        "sound": "ping",
        "title": "Hi",
        "body": "Ho",
        "image": ""
    },
    "notification": {
        "title": "",
        "body": "New message incoming..."
    },
    "to": "TOKEN",
    "apns": {
        "payload": {
            "aps": {
                "content-available": 1
            }
        },
        "headers": {
            "apns-priority": "5",
            "apns-push-type": "background",
            "apns-topic": "com.dakovo.ping"
        }
    },
    "android": {
    }
}

I receive content from "notification" but still app does not wake and does not trigger my background event listener

@mikehardy
Copy link
Collaborator

Why do you still have invalid JSON? That top content-available is incorrect. I have mentioned it multiple times. Fix your JSON.

When you send notification payload and data together it is called a "mixed" payload. Your app will not wake in that case. Have you examined the payload type vs app state vs handler charts?

https://rnfirebase.io/messaging/usage#message-handlers

@rahulje9
Copy link

rahulje9 commented Jan 4, 2021

Even I'm having issues, For me, I wasn't getting any notification on iOS for a long time and when I tried to test it with the REST API way, I got the push for about a week while I was testing things, and now that too stopped working.
I'm not sure what to do at this point. I'm posting the JSON payload I'm sending.

{
    "to": "FCM_TOKEN",
    "content_available": true,
    "mutable_content": true,
    "data": {
      "message": "Batman!",
      "mediaUrl": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/FloorGoban.JPG/1024px-FloorGoban.JPG"
    },
    "notification": {
      "body": "Enter your message",
      "sound": "default"
    }
  }

URL - https://fcm.googleapis.com/fcm/send

@mikehardy
Copy link
Collaborator

This issue is about silent ("data only") push. You have a notification chunk, so there is nothing silent about those. They are handled internally by firebase-ios-sdk and are posted to the notification center with no possibility of your handler getting control until the user interacts with the notification by tapping it. At that point the app starts (or is foregrounded) and the handler should be called if configured correctly

@rahulje9
Copy link

rahulje9 commented Jan 5, 2021

I've posted a test notification data which I got from StackOverflow, even with those I'm not getting the push notification all the time. I have got the notification for a while yesterday, from that I'm assuming that I have configured it correctly, please correct me if I'm wrong.

@nm-antecuic
Copy link

have anybody managed to fix this issue?
I'm losing my mind here. 🤣
foreground and background notifications are working as expected on android, and on iOS as well, except when app is killed.
then my data-only messages don't trigger notification at all.

@mikehardy
Copy link
Collaborator

@nm-antecuic there is no "fix" as this is not seen as a problem by Apple. Data-only messages do not have guaranteed delivery as a well-documented, intentional choice by Apple. If your app is killed and a data-only message is sent to it, it will probably never arrive.

Some more info that may or may not be useful (but you can watch logs to see iOS deciding not to start your app perhaps):
#4697 (comment)

For reliable delivery send a notification block in the JSON payload of your FCM, and if you want a chance to intercept it prior to posting the notification use a full-featured notification package with an app extension, as is documented for example in Notifee.app

@nm-antecuic
Copy link

@mikehardy so I have this feature where I need to send message to all app users and depending on somebody's location show them a notification about that. I thought this is a way to go since we can't store users location on server.
I can use Notifee.app for notifications and implement this feature, when notification is sent i can intercept it and show it to specific user no matter in which current state app is? right?

@mikehardy
Copy link
Collaborator

I have learned through hard experience to never offer certainty with regard to delivery of remote messages to mobile devices. You'll simply have to do the work to set up a test for your use case and collect results

@nm-antecuic
Copy link

@mikehardy I see. Thanks a lot for help! 👍🏼

@timothyerwin
Copy link

timothyerwin commented Dec 10, 2023

There is just no avoiding that, but the more a user uses your app and the more value they receive from it, the more likely they are to allow notifications and the more the power miser algorithm will grant the app wakeups etc., like whatsapp etc

@mikehardy do you know if this is how whatsapp is currently able to get messages delivered in the app while in background? the issue is I have a chat app and there is too much delay when opening the app and reading messages, so I need to be able to read them in the background at the time of the push notification.

I know that whatsapp historically had used voip unrestricted access but I believe apple since closed that loop?

I'm just trying to figure out how should I handle this? is messaging.setBackgroundMessageHandler the only way to handle this scenario today or am I missing something?

I've also read that conntent-available: 1 should be sent separately from a normal push notification? is this true? i.e., would I need to send 2, one for display and one for a silent notification?

sorry for all the questions, I'm just confused and don't want to waste a bunch of time going down a lot of rabbit holes if someone knows how this is handled today.

@mikehardy
Copy link
Collaborator

Use a real notification block with an extension helper for skinning/ branding on iOS, nothing else is reliable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help: needs-triage Issue needs additional investigation/triaging. type: bug New bug report
Projects
None yet
Development

No branches or pull requests

5 participants