customerio / customerio-reactnative

MIT License
23 stars 11 forks source link

Deeplinking issue on iOS #183

Closed manurodriguezDL closed 1 year ago

manurodriguezDL commented 1 year ago

SDK version: 3.1.5

Environment: Development or Production

Are logs available? This is all I see related to Customer.io / NotificationService (Installed the app on my phone, enabled notifications, user is identified, recieved a rich push with a deeplink and opened it):

(Sensitive information has been replaced with ---)

 [CIO] (siteid: --- ) ℹ️ Customer.io SDK 2.7.4 initialized and ready to use for site id: ---
 [CIO] (siteid: --- ) ℹ️ In-app module setup with SDK
NotificationServiceExtension    (siteid: --- ) 🛑 Received HTTP 400 response while trying to trackPushMetric. 400 responses never succeed and therefore, the SDK is deleting this SDK request and not retry. Error message from API: HTTP request responded with 400 - malformed delivery id: ., request data sent: 140 bytes

(In that log the siteId is the same that is mentioned at the CIO logs)

Describe the bug Cant get Deeplink to work on iOS. Android works great, but opening a push notification on iOS always takes me to the home screen of my app. I know the code related to react-navigation is fine since it navigates on Android. I also have Branch installed to handle other kind of Deeplinks, to which Im not sure if the implementation of Customer.io could be having conflicts with it. I need them to coexist, and I haven't found a way to do that yet (supposing the issue is with Branch in the first place).

To Reproduce My AppDelegate.mm:


@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  RCTAppSetupPrepareApp(application);
  // Workaround for opening deep-link with app closed
  NSMutableDictionary *modifiedLaunchOptions =
      [NSMutableDictionary dictionaryWithDictionary:launchOptions];
  if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
    NSDictionary *pushContent =
        launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
    if (pushContent[@"CIO"] && pushContent[@"CIO"][@"push"] &&
        pushContent[@"CIO"][@"push"][@"link"]) {
      NSString *initialURL = pushContent[@"CIO"][@"push"][@"link"];
      if (!launchOptions[UIApplicationLaunchOptionsURLKey]) {
          modifiedLaunchOptions[UIApplicationLaunchOptionsURLKey] = [NSURL URLWithString:initialURL];
      }
    }
  }
  // End workaround

  ...

  RCTBridge *bridge =
      [[RCTBridge alloc] initWithDelegate:self
                            launchOptions:modifiedLaunchOptions];

  NSString *intercomApiKey = [ReactNativeConfig envFor:@"IOS_INTERCOM_KEY"];
  NSString *intercomAppId = [ReactNativeConfig envFor:@"INTERCOM_APP_ID"];
  [IntercomModule initialize:intercomApiKey withAppId:intercomAppId];

  if ([[[UIDevice currentDevice] systemVersion]
          compare:@"15.0"
          options:NSNumericSearch] != NSOrderedAscending)
    [RNBranch.branch checkPasteboardOnInstall];
#ifdef DEBUG
  [RNBranch useTestInstance];
  [RNBranch setDebug];
  [RNBranch enableLogging];
#endif
  [RNBranch initSessionWithLaunchOptions:launchOptions isReferrable:YES];

  [self.window makeKeyAndVisible];

  // Create Object of class MyAppPushNotificationsHandler
  self.pushNotificationHandler = [[PushNotificationsHandler alloc] init];

  [RNSplashScreen show];

  [self.pushNotificationHandler initializeCioSdk];

  return YES;
}

- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  // Register device to receive push notifications with device token
  [self.pushNotificationHandler application:application
                                deviceToken:deviceToken];
}

- (void)application:(UIApplication *)application
    didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  [self.pushNotificationHandler application:application error:error];
}

// show push when the app is in foreground
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:
             (void (^)(UNNotificationPresentationOptions options))
                 completionHandler {
  completionHandler(UNNotificationPresentationOptionAlert +
                    UNNotificationPresentationOptionSound);
}

// handle push when the user responds to the notification
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
    didReceiveNotificationResponse:(UNNotificationResponse *)response
             withCompletionHandler:(void (^)(void))completionHandler {
  [self.pushNotificationHandler userNotificationCenter:center
                        didReceiveNotificationResponse:response
                                 withCompletionHandler:completionHandler];
}

...

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:
                (NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
  return [RCTLinkingManager application:app openURL:url options:options] || [RNBranch application:app openURL:url options:options];
}

- (BOOL)application:(UIApplication *)application
    continueUserActivity:(NSUserActivity *)userActivity
      restorationHandler:
          (void (^)(NSArray<id<UIUserActivityRestoring>> *_Nullable))
              restorationHandler {
  return [RNBranch continueUserActivity:userActivity] ||
         [RCTLinkingManager application:application
                   continueUserActivity:userActivity
                     restorationHandler:restorationHandler];
}

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

...

@end

I got my branch links at Associated Domains in my config, and the CIO one is the only one declared at URL Types at Info.plist, so universal links should be handled by the SDK.

PushNotificationHandler and NotificationServiceExtension are exactly like in the docs. I can confirm they are working since I can recieve rich pushes on my phone (testes with an image in my push, and I see it).

Expected behavior Deeplinking should work as it does on Android. I've already tried deleting all branch related code from AppDelegate and Info.plist and it still wont navigate on my phone. Would appreciate any other leads!

ami-aman commented 1 year ago

Thanks for reaching out to us @manurodriguezDL Apologies for the inconvenience the issue has caused.

I appreciate the very well detailed information about the issue. I see two questions in the detail. Let me address each of them, one by one!

manurodriguezDL commented 1 year ago

@ami-aman Thanks for the quick response!

ami-aman commented 1 year ago

Hi @manurodriguezDL

Thank you for your response. I appreciate it.

The 400 error that you are seeing is because test push notifications do not have a delivery ID in the payload. Delivery IDs are only used for production push notifications.

I understand that you are still facing two issues:

I will do my best to resolve both of these issues in this ticket.

Based on the code that you shared, I believe that you have not set the delegate for UNUserNotificationCenter. I recommend adding the following code snippet to your AppDelegate file in the didFinishLaunchingWithOptions method:

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

I strongly believe that adding the above snippet will help you fix the first issue i.e. not receiving push notifications when the app is in foreground and possibly deep links as well. I would suggest you to test both the issues after adding the snippet.

I am still investigating the second issue and will keep you updated on its progress. In the meantime, if you have any additional details to share, please feel free to add them to this ticket.

manurodriguezDL commented 1 year ago

@ami-aman That worked! Thank you for your suggestion.

It fixed both Deeplinking and foreground pushes.

I will be closing the issue now, thanks again for your input.

Regards, Manuel

SeongwoonHong commented 8 months ago

Hi @manurodriguezDL

Thank you for your response. I appreciate it.

The 400 error that you are seeing is because test push notifications do not have a delivery ID in the payload. Delivery IDs are only used for production push notifications.

I understand that you are still facing two issues:

  • You are not receiving push notifications when the app is in the foreground.
  • Deep links are not taking you to the designated screen.

I will do my best to resolve both of these issues in this ticket.

Based on the code that you shared, I believe that you have not set the delegate for UNUserNotificationCenter. I recommend adding the following code snippet to your AppDelegate file in the didFinishLaunchingWithOptions method:

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

I strongly believe that adding the above snippet will help you fix the first issue i.e. not receiving push notifications when the app is in foreground and possibly deep links as well. I would suggest you to test both the issues after adding the snippet.

I am still investigating the second issue and will keep you updated on its progress. In the meantime, if you have any additional details to share, please feel free to add them to this ticket.

I'm experiencing the exact same issue but still no luck with these changes :( Here's my AppDelegate.m

#import "AppDelegate.h"
#import <seong-Swift.h>

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <Firebase.h>
#import <React/RCTLinkingManager.h>

@implementation AppDelegate

MyAppPushNotificationsHandler* pnHandlerObj = nil;
+ (void)initialize {
  pnHandlerObj = [[MyAppPushNotificationsHandler alloc] init];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [FIRApp configure];

  RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [self.reactDelegate createRootViewWithBridge:bridge
                                                   moduleName:@"seong"
                                            initialProperties:nil];

  if (@available(iOS 13.0, *)) {
      rootView.backgroundColor = [UIColor systemBackgroundColor];
  } else {
      rootView.backgroundColor = [UIColor whiteColor];
  }

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [self.reactDelegate createRootViewController];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  // Set FCM messaging delegate
  [FIRMessaging messaging].delegate = self;
  // Call this before  calling registerPushNotification:self
  [pnHandlerObj initializeCioSdk];
  // Register for push notifications
  [pnHandlerObj registerPushNotification:self];
  [super application:application didFinishLaunchingWithOptions:launchOptions];

  // Set the delegate for UNUserNotificationCenter
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;

  return YES;
}

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

- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}

// firebase’s didReceiveRegistrationToken delegate method.
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
  [pnHandlerObj didReceiveRegistrationToken:messaging fcmToken: fcmToken];
}

// to show a notification when the app is in foreground
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
    completionHandler(UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionSound);
}

@end