davide-scalzo / react-native-mixpanel

A React Native wrapper for Mixpanel tracking
MIT License
455 stars 195 forks source link

Please update the notification payload to support the latest iOS #249

Open CassioMG opened 4 years ago

CassioMG commented 4 years ago

Context

We have successfully installed react-native-mixpanel on our project, it works perfectly for sending events and displaying push notifications. We also have FCM push notifications installed and running perfectly for either displaying or handling the push notification payload in this same project.

We are using the versions below:

"react": "16.10.2",
"react-native": "0.61.5",
"react-native-mixpanel": "^1.2.0",
"@react-native-firebase/messaging": "^7.2.1",

This is how our AppDelegate is looking with all its push notification listeners:

#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UNUserNotificationCenter.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>

@property (nonatomic, strong) UIWindow *window;

@end
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <Firebase.h>
#import "RNSplashScreen.h"
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>
#import <Mixpanel/Mixpanel.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [FIRApp configure];
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"vega"
                                            initialProperties:nil];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];

  [RNSplashScreen show];

  [Mixpanel sharedInstance].enableLogging = YES;

  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;

  return YES;
}

-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
   // Only listener called for Mixpanel pushes (apparently used only for displaying)
  completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{ 
   // Called only for FCM pushes
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
   // Called only for FCM pushes
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
   // Called only for FCM pushes
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
   // Called only for FCM pushes
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
   // Called only for FCM pushes
}

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
   // Called only for FCM pushes
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

@end

Issue

We need to be able to catch and read the Mixpanel notification payload when the app is either on foreground, background or closed. But apparently none of the iOS listeners catch the notification payload UNLESS we tap on it. So the notification is just displayed on device screen and that is all. On the other hand, the listeners work perfectly with our FCM push notifications WITHOUT needing to tap on them.

Below are the automatically generated Xcode logs we get when enabling [Mixpanel sharedInstance].enableLogging = YES;:

Proposed solution

Looks like Mixpanel push notifications only allows for notification displaying, which means it probably does not contain the latest APNS headers that need to be sent to the device in order for the device to receive the payload. You can check Apple Documentation here: https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns/

We had similar issue with FCM payload, and we solved it by using the block below including the contentAvailable and the correct apns-headers:

{
   "notification”:{
      "title":"Notification title",
      "body":"Notification body"
   },
   "apns":{
      "payload":{
         "aps":{
            "contentAvailable":true
         }
      },
      "headers":{
         "apns-push-type":"alert",
         "apns-priority":"5",
         "apns-topic": // your app bundle identifier
      }
   },
}

Could you please advise or update the notification payload to support the latest iOS?

Thanks!

CassioMG commented 4 years ago

Just discovered that adding a custom JSON for iOS (while creating the notification on Mixpanel Messages panel) like this solves this issue: Screen Shot 2020-08-19 at 17 01 12

The app now successfully reacts to the push notification whenever it's running on Foreground or Background. But I still don't know how to make things work if the app is quit (not running).