evollu / react-native-fcm

react native module for firebase cloud messaging and local notification
MIT License
1.73k stars 682 forks source link

_notificationType & opened_from_tray is undefined in iOS #927

Open juaan opened 6 years ago

juaan commented 6 years ago

Hi, I'm trying to implement the example that is in this repo, to my project. I'm handling the Listener's side. I get the notification, but some of the properties is undefined, like _notificationType, opened_from_tray and local_notification.

And some problem also happen the notification tray didn't pop up, unless I use FCM.presentLocalNotification. should I use that method even I want to catch the remote notification?

my current version:

RN : 0.51.0 RN-FCM : 14.1.2

evollu commented 6 years ago

you shouldn't need to. Are you experience those issue with example project?

juaan commented 6 years ago

Turns out it worked! I tried to send it manually from firebase console and it worked. The problem is my BE send data message notification type, instead of the default notification type. Could you please elaborate How to handle this data message notification type @evollu ?

evollu commented 6 years ago

In most cases, you should send iOS notifications with notification body. I remember reading somewhere that iOS is blocking data only notification in latest OS (I haven't validated yet). How to handle this data message notification type are you trying to do something with data only message?

juaan commented 6 years ago

img-20180516-wa0001

Yup, from what I read if it is data message, client need to do something to process the notification,

So i use fcm.showLocalNotificarion and it worked in foreground the notfication tray is popped, but when in background the notification tray didnt pop

Above is the object data message that I got from BE, its different with notification message @evollu

evollu commented 6 years ago

have you added background push capability in xcode? again, iOS should use notificatoin property to deliver reliably. Can you get what your BE is posting to firebase?

juaan commented 6 years ago

im still requesting from my BE engineer... @evollu

import FCM, {FCMEvent, RemoteNotificationResult, WillPresentNotificationResult, NotificationType, NotificationActionType, NotificationActionOption, NotificationCategoryOption} from "react-native-fcm";

import { pushNotifications } from '../services';

AsyncStorage.getItem('lastNotification').then(data=>{ if(data){ // if notification arrives when app is killed, it should still be logged here console.log('last notification', JSON.parse(data)); AsyncStorage.removeItem('lastNotification'); } })

AsyncStorage.getItem('lastMessage').then(data=>{ if(data){ // if notification arrives when app is killed, it should still be logged here console.log('last message', JSON.parse(data)); AsyncStorage.removeItem('lastMessage'); } }) const showLocalNotification = (data) => { FCM.presentLocalNotification({ id: new Date().valueOf().toString(), // (optional for instant notification) title: data.title, // as FCM payload body: data.body, // as FCM payload (required) sound: "bell.mp3", // "default" or filename priority: "high", // as FCM payload click_action: "com.myapp.MyCategory", // as FCM payload - this is used as category identifier on iOS. badge: 0, // as FCM payload IOS only, set 0 to clear badges icon: "ic_launcher", // as FCM payload, you can relace this with custom icon you put in mipmap show_in_foreground: true, // notification when app is in foreground (local & remote) opened_from_tray: 1 }); }

export function registerKilledListener(){ // these callback will be triggered even when app is killed FCM.on(FCMEvent.Notification, notif => { AsyncStorage.setItem('lastNotification', JSON.stringify(notif)); if(notif.opened_from_tray){ setTimeout(()=>{ if(notif._actionIdentifier === 'reply'){ if(AppState.currentState !== 'background'){ console.log('User replied '+ JSON.stringify(notif._userText)) alert('User replied '+ JSON.stringify(notif._userText)); } else { AsyncStorage.setItem('lastMessage', JSON.stringify(notif._userText)); } } if(notif._actionIdentifier === 'view'){ alert("User clicked View in App"); } if(notif._actionIdentifier === 'dismiss'){ alert("User clicked Dismiss"); } }, 1000) } }); }

// these callback will be triggered only when app is foreground or background export function registerAppListener(navigation){ FCM.on(FCMEvent.Notification, notif => { console.log("Notification", notif); // showLocalNotification(notif)
notif.finish(WillPresentNotificationResult.All) if (notif.label) showLocalNotification(notif)

if(notif.opened_from_tray){
  switch (notif.label) {
    case 'timeslots_ending':
      setTimeout(()=>{
        navigation.navigate('TimeslotList')
      }, 500)
      break;
    case 'confirm_meet':
      setTimeout(()=>{
        navigation.navigate('TimeslotList')
      }, 500)
      break;
    default:
      setTimeout(()=>{
        navigation.navigate('YourBids')
      }, 500)
      break;
  }
}

if(Platform.OS ==='ios'){
        //optional
        //iOS requires developers to call completionHandler to end notification process. If you do not call it your background remote notifications could be throttled, to read more about it see the above documentation link.
        //This library handles it for you automatically with default behavior (for remote notification, finish with NoData; for WillPresent, finish depend on "show_in_foreground"). However if you want to return different result, follow the following code to override
        //notif._notificationType is available for iOS platfrom
        switch(notif._notificationType){
          case NotificationType.Remote:
            notif.finish(RemoteNotificationResult.NewData); //other types available: RemoteNotificationResult.NewData, RemoteNotificationResult.ResultFailed
            // showLocalNotification(notif);
            break;
          case NotificationType.NotificationResponse:
            notif.finish();
            // showLocalNotification(notif);    
            break;
          case NotificationType.WillPresent:
            notif.finish(WillPresentNotificationResult.All);
            // showLocalNotification(notif);
            //other types available: WillPresentNotificationResult.None
            // this type of notificaiton will be called only when you are in foreground.
            // if it is a remote notification, don't do any app logic here. Another notification callback will be triggered with type NotificationType.Remote
            break;
          default:
            notif.finish(WillPresentNotificationResult.All);

            // showLocalNotification(notif);
            break;
        }
}

});

FCM.on(FCMEvent.RefreshToken, token => { console.log("TOKEN (refreshUnsubscribe)", token); });

FCM.enableDirectChannel(); FCM.on(FCMEvent.DirectChannelConnectionChanged, (data) => { console.log('direct channel connected ' + data); }); setTimeout(function() { FCM.isDirectChannelEstablished().then(d => console.log(d)); }, 1000); }

FCM.setNotificationCategories([ { id: 'bundle', actions: [ { type: NotificationActionType.TextInput, id: 'reply', title: 'Quick Reply', textInputButtonTitle: 'Send', textInputPlaceholder: 'Say something', intentIdentifiers: [], options: NotificationActionOption.AuthenticationRequired }, { type: NotificationActionType.Default, id: 'view', title: 'View in App', intentIdentifiers: [], options: NotificationActionOption.Foreground }, { type: NotificationActionType.Default, id: 'dismiss', title: 'Dismiss', intentIdentifiers: [], options: NotificationActionOption.Destructive } ], options: [NotificationCategoryOption.CustomDismissAction, NotificationCategoryOption.PreviewsShowTitle] } ])


- ## And this is how I call the listener

import {registerKilledListener, registerAppListener} from "./firebaseListener";

registerKilledListener(); class Nearby extends Component { .......

async componentDidMount() {

registerAppListener(this.props.navigation);
FCM.getInitialNotification().then(notif => {
  this.setState({
    initNotif: notif
  });

  if (notif && notif.label === 'new_bid'){
    setTimeout(()=>{
      this.props.navigation.navigate('YourBids')
    }, 500)
  };
});

if (this.props.logindata.userdata) {
  try{
    let result = await FCM.requestPermissions({badge: false, sound: true, alert: true});
  } catch(e){
    console.error(e);
  }

  FCM.getFCMToken().then(token => {
    console.log("TOKEN (getFCMToken)", token);
    this.props.registerFirebase({memberId: member_id, token})
  }); 
}

} }

evollu commented 6 years ago

your code seems fine BTW, you won't need following code if you are not doing special logic

if(Platform.OS ==='ios'){
            //optional
            //iOS requires developers to call completionHandler to end notification process. If you do not call it your background remote notifications could be throttled, to read more about it see the above documentation link.
            //This library handles it for you automatically with default behavior (for remote notification, finish with NoData; for WillPresent, finish depend on "show_in_foreground"). However if you want to return different result, follow the following code to override
            //notif._notificationType is available for iOS platfrom
            switch(notif._notificationType){
              case NotificationType.Remote:
                notif.finish(RemoteNotificationResult.NewData); //other types available: RemoteNotificationResult.NewData, RemoteNotificationResult.ResultFailed
                // showLocalNotification(notif);
                break;
              case NotificationType.NotificationResponse:
                notif.finish();
                // showLocalNotification(notif);    
                break;
              case NotificationType.WillPresent:
                notif.finish(WillPresentNotificationResult.All);
                // showLocalNotification(notif);
                //other types available: WillPresentNotificationResult.None
                // this type of notificaiton will be called only when you are in foreground.
                // if it is a remote notification, don't do any app logic here. Another notification callback will be triggered with type NotificationType.Remote
                break;
              default:
                notif.finish(WillPresentNotificationResult.All);

                // showLocalNotification(notif);
                break;
            }
    }
  });
juaan commented 6 years ago

hmmm what do you mean by special logic?

then again the notif still not showing when in background mode. am I doing it right?

do FCM.presentLocalNotification also make the notification show when the app is in background ?

@evollu

evollu commented 6 years ago

do FCM.presentLocalNotification also make the notification show when the app is in background ? not after going back to background for a while

temitope commented 5 years ago

@evollu Having somewhat similar issues, and since this wasnt closed figured I would add comment here rather than create a new issues unless/until i find out more.

my current version: react-native : 0.51.1 react-native-fcm : 13.3.1

I want to let people send messages via the push notification without brining the app to the foreground. (from killed or background state usually). I am having trouble being able to use the reply action consistently. I am using your example and specifically NotificationActionType.TextInput when calling setNotificationCategories. Everything seems to work fine as far as receiving a push and then opening it and typing a response. The FIRST time this happens and I reply, I get the expected behavior of an _actionIdentifier and expected _userText on the notification object.

However, things break down on the SECOND attempt at the same action being completed. Instead of the repeated previous behavior, nothing happens. It is almost as if the first action blocked or errored out perhaps. Only if I explicitly open the app and bring to the foreground does the intended action complete itself (for example: say userA and userB are having a conversation and many back and forth messages are happening via push. This wont support that without bringing app to foreground.)

Note that if I set the action options to NotificationActionOption.Foreground when calling setNotificationCategories everything works as expected, but it forces the app to the foreground each time a reply action is made. I desire to avoid that since it negates the convenience of not having to open the app in foreground to respond.

Question: Curious on how you make it so that the "AppKilledListener" one only fires when app is killed and not when app is in foreground. When I set it up they would both be fired in foreground since they are listening to same event but I just have to use logic know the state. Maybe that is an issue?

evollu commented 5 years ago

@temitope you might need to dig into the native code and see what is going on. Things might worth checking:

  1. does the first response call the iOS native callback correctly are you reply?
  2. Is second notification not received by the device at all or it is just not showing up? (check the delegate methods in AppDelegates.m)
temitope commented 5 years ago

@evollu i will take a closer look. thanks. For clarity

  1. Yes the first response calls works correctly as I can fully reply and all the notification data is present and working. (i assume this validates that the iOS native callback is happening correctly).
  2. The second time I try to use the push action of replying, the reply does not get ran by notification listeners. Unless I open the app.

Thanks.