Linking url event never fires

This issue has been tracked since 2022-09-15.

Description

Url event never fires

I followed the indicated setup to handle deep links in react native.

But when I have the app open in background or foreground and I execute command
npx uri-scheme open "mychat://bar" --ios, url event doesn't fire.

I saw there is a similar issue in react-navigation, but the fix indicated for AppDelegate.m doesn't work for me.

Version

0.70

Output of npx react-native info

System:
OS: macOS 12.5.1
CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 59.18 MB / 16.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 16.13.1 - /usr/local/opt/[email protected]/bin/node
Yarn: 1.22.11 - /usr/local/bin/yarn
npm: 8.1.2 - /usr/local/opt/[email protected]/bin/npm
Watchman: Not Found
Managers:
CocoaPods: 1.11.2 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5
Android SDK:
API Levels: 28, 31, 33
Build Tools: 30.0.2, 31.0.0, 33.0.0
System Images: android-31 | Google APIs Intel x86 Atom_64, android-31 | Google Play Intel x86 Atom_64
Android NDK: Not Found
IDEs:
Android Studio: 2021.2 AI-212.5712.43.2112.8815526
Xcode: 13.4.1/13F100 - /usr/bin/xcodebuild
Languages:
Java: 13.0.2 - /usr/bin/javac
npmPackages:
@react-native-community/cli: Not Found
react: 18.1.0 => 18.1.0
react-native: 0.70.0 => 0.70.0
react-native-macos: Not Found
npmGlobalPackages:
react-native: Not Found

Steps to reproduce

clone repo https://github.com/leoparis89/deeplink

  1. yarn
  2. cd ios && pod install
  3. yarn ios
  4. run command npx uri-scheme open "mychat://bar" --ios.
    App gets focused but url event never fires in ./useDeepLink.js line 15.

Snack, code example, screenshot, or link to a repository

https://github.com/leoparis89/deeplink

ariel-bentu wrote this answer on 2022-09-20

I want to add to this issue what I researched, having similar issue:

  • When the app is not running at all, and you select "share/open with" your app the following happens:
    • First, the (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions is called, having the path to the file in the launch options under "UIApplicationLaunchOptionsURLKey" key.
      This path is not accessible (permission-wise to the app).
      This path is also what you'd get if you call Linking.getInitialURL
    • Then, the (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:... is called.
      This method receives a different URL, which is a copy of the file in your app's temp/inbox folder - fully accessible to your app.
      And as suggested, it is implemented with one line: return [RCTLinkingManager application:application openURL:url options:options];, which triggers RCTLinkingManager to send notification via NSNotificationCenter with that (good) url.
      Now comes the issue: at this point in time, the JS was not yet started and the JS part of the app did not register to listen to "url" event. That is why RCTEventEmitter did not call RCTLinkingManager.startObserving and the notification with the url is lost. and even if it would have been received, the JS did not register yet, and it would not have been fired to the JS part.

Here is how I solved it (and I suggest you implement something more generic/robust for Linking:

  • when RCTLinkingManager application:application openURL:url options:options is called, I save the url in a object's local variable. the when RCTLinkingManager.startObserving is called, if this variable is not nil, I fire the url event.

  • line 19: static NSString *savedEvent = nil;
    at startObserving, at its end:

if (savedEvent != nil) {
        NSDictionary<NSString *, id> *payload = @{@"url": savedEvent};
        [self sendEventWithName:@"url" body:payload];
        savedEvent = nil;
    }

at application:openURL:options

    // if event was missed, save it for later firing
    savedEvent = URL.absoluteString;

I hope this helps.

leoparis89 wrote this answer on 2022-09-21

I want to add to this issue what I researched, having similar issue:

  • When the app is not running at all, and you select "share/open with" your app the following happens:

    • First, the (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions is called, having the path to the file in the launch options under "UIApplicationLaunchOptionsURLKey" key.
      This path is not accessible (permission-wise to the app).
      This path is also what you'd get if you call Linking.getInitialURL
    • Then, the (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:... is called.
      This method receives a different URL, which is a copy of the file in your app's temp/inbox folder - fully accessible to your app.
      And as suggested, it is implemented with one line: return [RCTLinkingManager application:application openURL:url options:options];, which triggers RCTLinkingManager to send notification via NSNotificationCenter with that (good) url.
      Now comes the issue: at this point in time, the JS was not yet started and the JS part of the app did not register to listen to "url" event. That is why RCTEventEmitter did not call RCTLinkingManager.startObserving and the notification with the url is lost. and even if it would have been received, the JS did not register yet, and it would not have been fired to the JS part.

Here is how I solved it (and I suggest you implement something more generic/robust for Linking:

  • when RCTLinkingManager application:application openURL:url options:options is called, I save the url in a object's local variable. the when RCTLinkingManager.startObserving is called, if this variable is not nil, I fire the url event.
  • line 19: static NSString *savedEvent = nil;
    at startObserving, at its end:
if (savedEvent != nil) {
        NSDictionary<NSString *, id> *payload = @{@"url": savedEvent};
        [self sendEventWithName:@"url" body:payload];
        savedEvent = nil;
    }

at application:openURL:options

    // if event was missed, save it for later firing
    savedEvent = URL.absoluteString;

I hope this helps.

Could you specify in which file those modifications need to be made ?
Ideally could you also post a snippet of the modified code ?

Haven't been able to reproduce your instructions.
Thanks

ariel-bentu wrote this answer on 2022-09-22

The file is react-native/Libraries/LinkingIOS/RCTLinkingManager.mm

#import <React/RCTLinkingManager.h>

#import <FBReactNativeSpec/FBReactNativeSpec.h>
#import <React/RCTBridge.h>
#import <React/RCTUtils.h>
#import <React/RCTLog.h>

#import "RCTLinkingPlugins.h"

static NSString *const kOpenURLNotification = @"RCTOpenURLNotification";

static NSString *savedEvent = nil;
- (void)startObserving
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleOpenURLNotification:)
                                                 name:kOpenURLNotification
                                               object:nil];
    if (savedEvent != nil) {
        NSDictionary<NSString *, id> *payload = @{@"url": savedEvent};
        [self sendEventWithName:@"url" body:payload];
        savedEvent = nil;
    }
}
+ (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)URL
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
    postNotificationWithURL(URL, self);
    
    // if event was missed, save for firing later if event "url" is registered
    savedEvent = URL.absoluteString;
    
    return YES;
}
edwardhsueh wrote this answer on 2022-09-22

@ariel-bentu
I try you solution , but url event doesn't fire when I have the app open in background or foreground.
(when app closed , it's ok).
Do you have any suggestion ?

ariel-bentu wrote this answer on 2022-09-22

do you have this method added in your AppDelegate.mm:

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

This is a prerequisite, also documented by react native (see here)

edwardhsueh wrote this answer on 2022-09-23

Yes, I do follow the document but still fail. I can't run in simulator, so I use browser with button to call the url scheme.
if app is closed, it works fine. But if app is in background, the url scheme called just bring app to foreground, but the url listener is not fired.
My RN is 0.68.2, older than yours. Will this be a problem?
By the way, can your app receive url event when it is in background?

More Details About Repo
Owner Name facebook
Repo Name react-native
Full Name facebook/react-native
Language JavaScript
Created Date 2015-01-09
Updated Date 2022-10-03
Star Count 105144
Watcher Count 3666
Fork Count 22469
Issue Count 2232

YOU MAY BE INTERESTED

Issue Title Created Date Updated Date