react-native
react-native copied to clipboard
Linking url event never fires
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/node@16/bin/node Yarn: 1.22.11 - /usr/local/bin/yarn npm: 8.1.2 - /usr/local/opt/node@16/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
- yarn
- cd ios && pod install
- yarn ios
- run command
npx uri-scheme open "mychat://bar" --ios. App gets focused but url event never fires in./useDeepLink.jsline 15.
Snack, code example, screenshot, or link to a repository
https://github.com/leoparis89/deeplink
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 *)launchOptionsis 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 callRCTLinkingManager.startObservingand 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.
- First, the
Here is how I solved it (and I suggest you implement something more generic/robust for Linking:
-
when
RCTLinkingManager application:application openURL:url options:optionsis called, I save the url in a object's local variable. the whenRCTLinkingManager.startObservingis 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.
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 *)launchOptionsis 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 callRCTLinkingManager.startObservingand 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:optionsis called, I save the url in a object's local variable. the whenRCTLinkingManager.startObservingis 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
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;
}
@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 ?
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)
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?
Hi @edwardhsueh, I was using react native 0.68.0 and facing the same problem One thing that you can try is adding the below code to the location that specified by React Navigation Documentation as I am not familiar with Swift and placing the code in the wrong location
// Add the header at the top of the file:
#import <React/RCTLinkingManager.h>
// Add this inside `@implementation AppDelegate` above `@end`:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
It's working for me now
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; }
This solution is not working for me either.
I added the lines you described in RTCLinkingManager.mm.
I added the lines in AppDelegate.mm as described in RN Linking docs.
Is there possibly some extra setup to do?
I find it surprising that a major feature like deeplinking is currently broken. Any clue on a possible workaround?
Hi, Finally, I found that linking works correctly on iOS/android when I upgrade react-native from 0.68 to 0.70.1 and follow the setting requirement for Linking
Hi, Finally, I found that linking works correctly on iOS/android when I upgrade react-native from 0.68 to 0.70.1 and follow the setting requirement for Linking
Does does it work when app is open in background ? Or only when it was previously closed ?
On my side, even with RN 0.70.6 url event never fires when app is already in background.
Hi, Finally, I found that linking works correctly on iOS/android when I upgrade react-native from 0.68 to 0.70.1 and follow the setting requirement for Linking
Could you post a link to a repo with the working config? I tried the specific version 0.70.1 and url event still doesn't fire if app is open in background.
@leoparis89 :
the following is my react related repo.
"react": "18.1.0",
"react-native": "0.70.1",
I have url event fired in background by using
using Linking.addEventListener('url', callback)
Does your problem occurs both in iOS and Android platform ?
Do we have some update? I have same problem.It works on android in killed and background state but on iOS doesn't work in background/foreground. My enviroment is : "react": "18.0.0", "react-native": "0.70.6" I use also some firebase packeges like (react-native-firebase/auth,react-native-firebase/app...) could this be a problem?
Solved. It was an error on my part:
the bellow snippet must be added in @implementatione AppDelegat not @interface AppDelegate
// For deep links
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
// For universal links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
Solved. It was error in my project. After upgrade of React Native version, I forgot to delete old settings file for iOS. (AppDelegat.m) So, in project were AppDelegat.m and AppDelegat.mm. New version of React Native needs file with new extension .mm instead of .m and this caused problems with deep linking. When I removed old file with extension .m and move all logic for deep linking in file with extension .mm I got BINGO !!!
@SerafinAna I upgrade to RN 0.71.6 and move the deep linking code from AppDelegate.m to AppDelegate.mm but the link is still unable to open i got this
Error opening URL: [Error: Unable to open URL: com.xxxxx://dev-xx.com/ios/com.xxxxx/login-callback?code=xxxxx. Add com.xxxxx to LSApplicationQueriesSchemes in your Info.plist.]
here is my AppDelegate.mm in case I missed out anything
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:app
openURL:url
options:options];
}
// For universal links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"nativeIntegration";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
return true;
}
@end
@ariel-bentu how can you modified the library node_modules/react-native/Libraries/LinkingIOS/RCTLinkingManager.mm
this file will be restored when I run yarn
anyway, it will cause this build error in xcode Use of undeclared identifier 'savedEvent'
so this is deep linking broken issue is still an issue in react native I am using
"react": "18.2.0", "react-native": "^0.71.6",
We're affected as well by the bug.
can we reopen this issue? this issue is not resolved yet
I also faced the same problem. In Android works but in iOS Linking.addEventListener is not working.
My RN Version: "0.70.6"
I manage to get it work on iOS need to add the url to the xcode setting refer to https://reactnavigation.org/docs/deep-linking/. At xcode, go to Targets, under the Info tab, go to URL Types, add the appID to the identifier and URL Scheme field the appID would be com.RNProject
however my android does not work
I got a build error on android android/app/src/main/java/com/xxx/usermanagement/testapp/MainApplication.java:61: error: cannot find symbol ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); ^ symbol: variable ReactNativeFlipper location: class MainApplication 1 error
if you use react native +0.71
change AppDelegate.m to AppDelegate.mm
// Add the header at the top of the file:
#import <React/RCTLinkingManager.h>
// Add this inside `@implementation AppDelegate` above `@end`:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
// Add this inside `@implementation AppDelegate` 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];
}
I have the same issue on android - Linking.addEventListener('url', callback) doesn't fire callback when the app in background. "expo": "~48.0.18", "react-native": "0.71.8", "react": "18.2.0"
Does anyone have decision?