1

Hello and thank you in advance for your help.

I'm experiencing a problem in ios when the app is closed (killed) and a notification with a deep link is received: the url of the deep link is not passed to the app. If the app is in background state everything works fine. If the deep link is contained in an email everything works fine both for background and killed state.

What am I missing?

This is my AppDelegate.m implementation:

#import "AppDelegate.h"

#import <React/RCTLinkingManager.h>

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

#import "RNBootSplash.h"

#import <MarketingCloudSDK/MarketingCloudSDK.h>
#import <MarketingCloudSDK/MarketingCloudSDKConfigBuilder.h>

#import "moncler-Swift.h"

#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>

static void InitializeFlipper(UIApplication *application) {
  FlipperClient *client = [FlipperClient sharedClient];
  SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
  [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
  [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
  [client addPlugin:[FlipperKitReactPlugin new]];
  [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
  [client start];
}
#endif

@import UIKit;
@import Firebase;

#import <GoogleMaps/GoogleMaps.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [GMSServices provideAPIKey:@"BLABLABLA"];// Production

#ifdef FB_SONARKIT_ENABLED
  InitializeFlipper(application);
#endif

  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"BLABLABLA"
                                            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];

  [RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView];

  [FIRApp configure];

  // BEGIN: MARKETING CLOUD SDK CONFIGURATION
  // configure the Marketing Cloud SDK ...

  NSString *appID= @"";
  NSString *accessToken = @"";
  NSString *appEndpoint = @"";

  #ifdef DEBUG
    // DEVELOPMENT
    appID = @"BLABLABLA";
    accessToken = @"BLABLABLA";
    appEndpoint = @"BLABLABLA";
  #else
    // PRODUCTION
    appID = @"BLABLABLA";
    accessToken = @"BLABLABLA";
    appEndpoint = @"BLABLABLA";
  #endif

  MarketingCloudSDKConfigBuilder *mcsdkBuilder = [MarketingCloudSDKConfigBuilder new];
  [mcsdkBuilder sfmc_setApplicationId:appID];
  [mcsdkBuilder sfmc_setAccessToken:accessToken];
  [mcsdkBuilder sfmc_setAnalyticsEnabled:@(YES)];
  [mcsdkBuilder sfmc_setMarketingCloudServerUrl:appEndpoint];

  NSError *error = nil;
  BOOL success =
      [[MarketingCloudSDK sharedInstance] sfmc_configureWithDictionary:[mcsdkBuilder sfmc_build]
                                                                 error:&error];
  [[MarketingCloudSDK sharedInstance] sfmc_setURLHandlingDelegate:self];

  if (success == YES) {
      dispatch_async(dispatch_get_main_queue(), ^{
        if (@available(iOS 10, *)) {
            // set the UNUserNotificationCenter delegate - the delegate must be set here in
            // didFinishLaunchingWithOptions
            [UNUserNotificationCenter currentNotificationCenter].delegate = self;
            [[UIApplication sharedApplication] registerForRemoteNotifications];


            [[UNUserNotificationCenter currentNotificationCenter]
                requestAuthorizationWithOptions:UNAuthorizationOptionAlert |
                                                UNAuthorizationOptionSound |
                                                UNAuthorizationOptionBadge
                              completionHandler:^(BOOL granted, NSError *_Nullable error) {
                                if (error == nil) {
                                    if (granted == YES) {
                                        dispatch_async(dispatch_get_main_queue(), ^{
                                                       });
                                    }
                                }
                              }];
        } else {
          #if __IPHONE_OS_VERSION_MIN_REQUIRED < 100000
            UIUserNotificationSettings *settings = [UIUserNotificationSettings
                settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound |
                                 UIUserNotificationTypeAlert
                      categories:nil];
            [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
          #endif
            [[UIApplication sharedApplication] registerForRemoteNotifications];
        }
      });
  } else {
      //  MarketingCloudSDK sfmc_configure failed
      os_log_debug(OS_LOG_DEFAULT, "MarketingCloudSDK sfmc_configure failed with error = %@",
                   error);
  }

  // END: MARKETING CLOUD SDK CONFIGURATION

  return YES;
}

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


- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [[MarketingCloudSDK sharedInstance] sfmc_setDeviceToken:deviceToken];
}

- (void)application:(UIApplication *)application
    didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    os_log_debug(OS_LOG_DEFAULT, "didFailToRegisterForRemoteNotificationsWithError = %@", error);
}

// The method will be called on the delegate when the user responded to the notification by opening
// the application, dismissing the notification or choosing a UNNotificationAction. The delegate
// must be set before the application returns from applicationDidFinishLaunching:.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
    didReceiveNotificationResponse:(UNNotificationResponse *)response
             withCompletionHandler:(void (^)(void))completionHandler {
    // tell the MarketingCloudSDK about the notification
    [[MarketingCloudSDK sharedInstance] sfmc_setNotificationRequest:response.notification.request];

    if (completionHandler != nil) {
        completionHandler();
    }
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:
             (void (^)(UNNotificationPresentationOptions options))completionHandler {
    NSLog(@"User Info : %@", notification.request.content.userInfo);
    completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

// This method is REQUIRED for correct functionality of the SDK.
// This method will be called on the delegate when the application receives a silent push

- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
          fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    UNMutableNotificationContent *theSilentPushContent =
        [[UNMutableNotificationContent alloc] init];
    theSilentPushContent.userInfo = userInfo;
    UNNotificationRequest *theSilentPushRequest =
        [UNNotificationRequest requestWithIdentifier:[NSUUID UUID].UUIDString
                                             content:theSilentPushContent
                                             trigger:nil];

    [[MarketingCloudSDK sharedInstance] sfmc_setNotificationRequest:theSilentPushRequest];

    completionHandler(UIBackgroundFetchResultNewData);
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  BOOL handledAdyen =[AdyenObjectiveCBridge applicationDidOpenURL:url];
  NSLog(@"Adyen opn url response : %@", handledAdyen ? @"YES" : @"NO");
  return [RCTLinkingManager application:application openURL:url options:options];
}

// Add this above `@end`:
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
 restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
 return [RCTLinkingManager application:application
                  continueUserActivity:userActivity
                    restorationHandler:restorationHandler];
}

- (void)sfmc_handleURL:(NSURL *)url type:(NSString *)type {
  NSLog(@"Handling open direct url coming from Marketing Cloud");
  AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
  NSDictionary *dict = [NSDictionary dictionary];
  [appDelegate application:[UIApplication sharedApplication] openURL:url options:dict];
}

@end
2
  • 1
    Are you able to test in your application:didReceiveRemoteNotification:fetchCompletionHandler: function that the url is present in the notification? I useuserDefaults to test this quickly from a terminated state. Also, is there a particular reason you are creating a new notification instead of just passing the userInfo directly to the SDK via -sfmc_setNotificationUserInfo:? Jun 16, 2021 at 18:13
  • Where am I creating a new notification? Do you have an example of a working AppDelegate for my use case?
    – LucaEffe
    Jun 16, 2021 at 19:06

1 Answer 1

2

I solved my problem thanks to this comment: https://github.com/facebook/react-native/issues/24429#issuecomment-858699356

Hello, In my case the problem appears only with deeplink into push notifications (app open by click on notification). I use a workaround in AppDelegate > didFinishLaunchingWithOptions. This patch allow to "translate" UIApplicationLaunchOptionsRemoteNotificationKey into UIApplicationLaunchOptionsURLKey. In that way RN Linking can consume it and then getInitialURL return the good value :

 NSMutableDictionary *newLaunchOptions = [NSMutableDictionary dictionaryWithDictionary:launchOptions];
  if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
      NSDictionary *remoteNotif = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
    
      // here goes specific selection according to your payload structure 
     // exemple here: { MySubObject : { MysDeeplinkUrlKey : 'my.url.scheme://zizou?number=10' }}
      if (remoteNotif[@"MySubObject"] && remoteNotif[@"MySubObject"][@"MysDeeplinkUrlKey"]) {
          NSString *initialURL = remoteNotif[@"MySubObject"][@"MysDeeplinkUrlKey"];
          if (!launchOptions[UIApplicationLaunchOptionsURLKey]) {
              newLaunchOptions[UIApplicationLaunchOptionsURLKey] = [NSURL URLWithString:initialURL];
          }
      }
  }

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

Hope it can help someone :)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .