Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep Link is not handled when app is closed React Native iOS app

I'm developing a React Native app for iOS.

This app has to be able to open deeplinks and it works fine when the app is opened in background. When the app is close, the native iOS code (Objective-C) does not get the URL the app has been opened with.

As I understand how it works, I have to check the NSDictionary of the launchOptions to see if the app has been started through an URL. If the key corresponding to URL initialization exists then I return true to execute the following code

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

  return YES;
}

This is the function that has to bee executed to get the Initial Url which the app has been opened with. Here is my code of app didFinishWithLaunchOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary  *)launchOptions
{
    if ([launchOptions objectForKey:UIApplicationLaunchOptionsURLKey]) {
    return true;
  }
}
like image 412
Quim Baget Avatar asked Mar 27 '19 19:03

Quim Baget


People also ask

How do deep links work in iOS?

Deep link is a technology that launches an app and opens a specific page in the app once the user clicks a URL on a web page or in another app. Implementing deep links is an exciting way to enhance the user experience by seamlessly allowing users to access the specific page without interruption.

How do I defer a deep link?

Prompt a non-app user on a smartphone to download the app before directing to specific page or content within the app when the launch it. Direct a user on a desktop or laptop computer to an appropriate page on your website instead of mobile app. Provide attribution data when each link has been clicked.

How to handle deep linking in a React Native iOS app?

How to Handle Deep Linking in a React Native Ios App? 1 React Native projects setup in iOS : Configure iOS application to open based on the mymoc:// URI scheme. ... 2 Configuration : In this article we will see how to handle external URIs and after that get a call back from that URIs and move to a specific page in ... 3 References: More items...

How do I get notified of incoming deep links?

If the app wasn't previously open, the deep link needs to set the initial state If the app was already open, the deep link needs to update the state to reflect the incoming link React Native provides a Linking to get notified of incoming links. React Navigation can integrate with the Linking module to automatically handle deep links.

What is deep linking in mobile app?

Before we start I would like to explain what deep linking is. Mobile app deep links (also known simply as “deep links”) point to content inside an app. It is useful sometimes to navigate user to a certain screen on app start rather than open a home screen. A general structure of a deep link is the following schemename://path?query_string.

What is deep linking in JavaScript?

It uses only JavaScript to build a mobile application. It is like React, which uses native components rather than using web components as building blocks. Deep Linking is a technique in which a given URL is used to open a specific page or specific screen in a mobile app, providing a better experience.


2 Answers

The core issue of Intercom not handling deep links for me was that react-native-intercom does not pass an initial URL to an application through didFinishLaunchingWithOptions if it's opened from the background.

Instead react-native-intercom calls the openURL method immediately after the application is launched and the react-native part of the application misses this event.

The solution for this was to create a new thread in the openURL method. Lock it to stop the execution and then unlock the tread when React application gives a signal to the native code, that it's ready to handle deep link. The fix is slightly complicated, so I've created a separate repository, describing the problem and the solution.

The core idea can be described in code as:

dispatch_queue_t queue = dispatch_queue_create("<yourAppName>.openUrlQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
  while (!DeepLink.canHandleDeepLinks) {
    [DeepLink.canHandleDeepLinksLock wait];
  }
  [DeepLink.canHandleDeepLinksLock unlock];


  dispatch_async(dispatch_get_main_queue(), ^{
    // This method call will trigger the Linking event with URL to be dispatched to your 'javascript' code
    [RCTLinkingManager application:application openURL:url options:options];
  });
});

I've created a repository with the solution to this problem. Go, check it out.

like image 127
Vlad Sereda Avatar answered Sep 26 '22 16:09

Vlad Sereda


While definitely not an answer, I've (terribly) worked around this issue (at least until a canonical solution is available) by changing (or if you don't already have, adding) the - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler { in AppDelegate.m to:

- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
 restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
  NSURL *url = [userActivity webpageURL];
  BOOL result = [RCTLinkingManager application:application
                          continueUserActivity:userActivity
                            restorationHandler:restorationHandler];
  if([userActivity webpageURL]){
    #if DEBUG
    float seconds = 3.5;
    #else
    float seconds = 1.5;
    #endif
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      NSURL *newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"MYAPPSCHEME:/%@", url.path]];
      [[UIApplication sharedApplication] openURL:newUrl options:@{} completionHandler:nil];
    });
  }
  
  return result;
}

It basically checks if there's a URL available that caused the launch, and invokes an open URL call to the app after "things have settled down" (as in debug mode it takes longer to load, I've changed to 3.5 seconds which handles it just well). Change the seconds and of course MYAPPSCHEME accordingly. And YES, there is a single slash (/) after MYAPPSCHEME: instead of double as url.path seems to have a leading slash already. The reason for replacing http[s] with my app scheme is that for some reason it launches Safari instead of handling deep link if I leave http[s]:// (which is not the case when app is already running and an URL is handled). My app handles custom schemes the same way with regular http links so it works well, but make sure you set it up accordingly for it to work.

After making the above changes and recompiling (don't forget that you're at Objective-C part, not JS/React Native) it worked. I'd love to see an actual solution rather than a hacky workaround, but until then, this fixed it for me.

like image 38
Can Poyrazoğlu Avatar answered Sep 24 '22 16:09

Can Poyrazoğlu