davide-scalzo / react-native-mixpanel

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

iOS deep link in push notification: no visible @interface for 'Mixpanel' declares the selector 'trackPushNotification:' #187

Open pleportz opened 5 years ago

pleportz commented 5 years ago

Hi

I'd like my app to open a deep link when opening a push notification. It works on android but not on iOS yet. I followed this guide https://help.mixpanel.com/hc/en-us/articles/115004617366-Send-Custom-Data-In-Push-Notifications. It specifies that I should add the following to my AppDelegate.m:

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo{
    [[Mixpanel sharedInstance] trackPushNotification:userInfo];
    NSString *web_link = [userInfo objectForKey:@"web_link"];
    NSURL *url = [[NSURL alloc] initWithString:web_link];
    if (url) {
      [[UIApplication sharedApplication] openURL:url];
    }
}

When I run react-native run-ios I get the following error when compiling AppDelegate.m

no visible @interface for 'Mixpanel' declares the selector 'trackPushNotification:'

I checked react-native-mixpanel files: the trackPushNotification method is declared in https://github.com/davodesign84/react-native-mixpanel/blob/master/RNMixpanel/Mixpanel.h but the file that should implement it is missing (there is no RNMixpanel/Mixpanel.m file).

Any help would be appreciated :)

craigcoles commented 4 years ago

@pleportz Did you ever find a solution for this? I am currently running into the exact same issue.

Looking at trackPushNotification method in RNMixpanel/Mixpanel.h, I am guessing it just needs to be exposed in RNMixpanel/RNMixpanel.m like the other methods.

Also interested to see your Android implementation too 😄

pleportz commented 4 years ago

@craigcoles I did not try further to find a solution for iOS after posting the issue. Regarding the android implementation, I followed react-native-mixpanel installation guide (do not forget the Additional info for android). My app navigation is handled by react-navigation so I used the uriPrefix prop to handle the deeplink.

craigcoles commented 4 years ago

@pleportz I got in touch with Mixpanel support regarding this and I got a response:

The trackPushNotification function would not be the relevant part to allow you to see the proper screen. That function just tracks the fact that the push was acted upon.

You will want to set up a listener in the receiver (didReceiveRemoteNotification). You will then want to look at the format of the userInfo dictionary you receive, and then open that URL if there is one: [[UIApplication sharedApplication] openURL:url];

Based on this, you should not really need to expose additional methods from the SDK (although having this one would be nice so you could also easily track touching that push).

I am hoping to revisit the project I was working on and make the change. I will report back with the result 👍

abeddow91 commented 4 years ago

@pleportz - How did you structure your custom payload for Android? Mine doesn't seem to be reading 'mp_cta'

thanks!!

0hio-creator commented 4 years ago

@craigcoles Did you ever progress with your implementation for IOS?

craigcoles commented 4 years ago

@0hio-creator I didn't really manage to progress much further with this issue unfortunately.

I can share some of the code I put together, to hopefully help someone else move forward with this. However, the code I am sharing does cause a crash of the app when opening a push notification that has a deep link:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
      [self application:application didReceiveRemoteNotification:launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]];
  }
  ...
  return YES;
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  NSString *deep_link = [userInfo objectForKey:@"deep_link"];
  NSURL *url = [[NSURL alloc] initWithString:deep_link];
  if (url){
       [[UIApplication sharedApplication] openURL:url];
  }
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

The crash message I was seeing is:

-[AppDelegate application:didReceiveRemoteNotification:]: unrecognized selector sent to instace 0x280358680

I would highly recommend getting in touch with Mixpanel directly, I was having a back and forth with a Senior Support Engineer (massive props to Argenis), we didn't manage to get to the bottom of this due to time limitations. However, they were pretty keen to get to the bottom of this issue for understanding and knowledge on their part too.

0hio-creator commented 4 years ago

@craigcoles Cheers for your help!. I got a solution working

In AppDelegate.m

Declare UNUserNotificationCenter. You can request push notification permission here. But i am using react native firebase to do so. Both options work. SImilairly I'm handling registering a token in js https://github.com/davodesign84/react-native-mixpanel/issues/234

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        center.delegate = self;
/*[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if( !error ) {
            // required to get the app to do anything at all about push notifications
            dispatch_async(dispatch_get_main_queue(), ^{
                [[UIApplication sharedApplication] registerForRemoteNotifications];
            });
            NSLog( @"Push registration success." );
        } else {
          NSLog( @"Push registration FAILED" );
          NSLog( @"ERROR: %@ - %@", error.localizedFailureReason, error.localizedDescription );
          NSLog( @"SUGGESTIONS: %@ - %@", error.localizedRecoveryOptions, error.localizedRecoverySuggestion );
        }*/

Handling the notification package and passing to open url

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
    NSLog( @"Handle push from background or closed" );
    // if you set a member variable in didReceiveRemoteNotification, you  will know if this is from closed or background
    NSLog(@"%@", response.notification.request.content.userInfo);
    NSString *web_link = [response.notification.request.content.userInfo valueForKeyPath:@"mp_ontap.uri"];
    NSURL *url = [[NSURL alloc] initWithString:web_link];
    NSLog(@"url: %@", url);
    if (url) {
        [[UIApplication sharedApplication] openURL:url];
    }
    completionHandler(response);
}

My app delegate.h

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

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

@property (nonatomic, strong) UIWindow *window;

@end

I could never get it to work with didReceiveRemoteNotification which is actually deprecated. UNUserNotificationCenter is the current method and have set to ensure it is handling incoming notifications