React-native: React native deep linking not working when app is in background state

Created on 26 Apr 2019  Ā·  43Comments  Ā·  Source: facebook/react-native

šŸ› Bug Report


When the app is closed, I am able to get deep link url that is clicked by Linking.getInitialURL(). When the app is in the background state, then nothing is mounted. So, I am not able to get the url even by the Linking.addEventListener('url', method_name).

What is the way to achieve this?

To Reproduce

Expected Behavior

Code Example


componentDidMount() {
Linking.addEventListener('url', this._handleOpenURL);
},
componentWillUnmount() {
Linking.removeEventListener('url', this._handleOpenURL);
},
_handleOpenURL(event) {
console.log(event.url);
}
I have added this code in app.js

Environment


React Native Environment Info:
System:
OS: Linux 4.15 Ubuntu 18.04.1 LTS (Bionic Beaver)
CPU: (4) x64 Intel(R) Core(TM) i3-6098P CPU @ 3.60GHz
Memory: 1.73 GB / 15.57 GB
Shell: 4.4.19 - /bin/bash
Binaries:
Node: 10.11.0 - /usr/bin/node
npm: 6.7.0 - /usr/bin/npm
npmPackages:
react: 16.6.0-alpha.8af6728 => 16.6.0-alpha.8af6728
react-native: ^0.58.5 => 0.58.5
npmGlobalPackages:
create-react-native-app: 2.0.2
react-native-cli: 2.0.1
react-native-rename: 2.4.0
react-native-slideshow: 1.0.1

Linking Bug Verify on Latest Version Linux

Most helpful comment

Pls try this.
componentDidMount() {
Linking.addEventListener("url", this.handleOpenURL);
this.handleDeepLinkingRequests();
}
handleDeepLinkingRequests = () => {
Linking.getInitialURL()
.then(url => {
if (url) {
this.handleOpenURL(url);
}
})
.catch(error => { // Error handling });
}
};

handleOpenURL = (url) => {
// your navigation logic goes here
}

Notes:
-> Linking.getInitialURL() method should only be called for the first time when the app is launched via app-swap
-> For subsequent app-swap calls, handleOpenURL() method will be called as it is configured with linking event listener.
-> remember to unsubscribe linking events in componentwillunmount()

All 43 comments

Can you run react-native info and edit your issue to include these results under the Environment section?

If you believe this information is irrelevant to the reported issue, you may write [skip envinfo] alongside an explanation in your Environment: section.

It looks like you are using an older version of React Native. Please update to the latest release, v0.59 and verify if the issue still exists.

The "Resolution: Old Version" label will be removed automatically once you edit your original post with the results of running react-native info on a project using the latest release.

are you added

<activity
  android:name=".MainActivity"
  android:launchMode="singleTask">

in AndroidManifest.xml ?
(https://facebook.github.io/react-native/docs/linking)

Yes, I have already added this in 'my_project/android/app/src/main/AndroidManifest.xml. Example of code:

<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTask">

Any updates on this?

@DalbirKaur were you able to solve for this?

@platonish, I am not able to get any lead yet.

I'm having the same issue in android I'm not able to get the URL to make any action
"react": "^16.8.3",
"react-native": "^0.59.2",

@DalbirKaur can you attach AndroidManifest.xml file ?

package="com.turndoctor">









android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />

<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<application
  android:name=".MainApplication"
  android:label="@string/app_name"
  android:icon="@mipmap/ic_launcher_round"
  android:allowBackup="false"
  android:theme="@style/AppTheme">
  <activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
      android:screenOrientation="portrait"
    android:windowSoftInputMode="adjustResize"
      android:launchMode="singleTask">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
            <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
        </intent-filter>
      <intent-filter>
          <data android:scheme="http" android:host="MY_HOST_NAME" />
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.BROWSABLE"/>
          <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
      <intent-filter android:label="@filter_view_http_gizmos">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <!-- Accepts URIs that begin with "http://www.example.com/gizmosā€ -->
          <data android:scheme="http"
              android:host="www.tdemo.com"
              android:pathPrefix="/td" />
          <!-- note that the leading "/" is required for pathPrefix-->
      </intent-filter>
      <intent-filter android:label="Turn Doctor">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <!-- Accepts URIs that begin with "example://gizmosā€ -->
          <data android:scheme="example"
              android:host="gizmos" />
      </intent-filter>
  </activity>
  <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    <meta-data  android:name="com.dieam.reactnativepushnotification.notification_channel_name"
        android:value="YOUR NOTIFICATION CHANNEL NAME"/>
    <meta-data  android:name="com.dieam.reactnativepushnotification.notification_channel_description"
        android:value="YOUR NOTIFICATION CHANNEL DESCRIPTION"/>
    <!-- Change the resource name to your App's accent color - or any other color you want -->
    <meta-data  android:name="com.dieam.reactnativepushnotification.notification_color"
        android:resource="@android:color/white"/>

    <!-- < Only if you're using GCM or localNotificationSchedule() > -->
    <receiver
        android:name="com.google.android.gms.gcm.GcmReceiver"
        android:exported="true"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="${applicationId}" />
        </intent-filter>
    </receiver>
    <!-- < Only if you're using GCM or localNotificationSchedule() > -->

    <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
    <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    <service android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationRegistrationService"/>

    <!-- < Only if you're using GCM or localNotificationSchedule() > -->
    <service
        android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerServiceGcm"
        android:exported="false" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        </intent-filter>
    </service>
    <!-- </ Only if you're using GCM or localNotificationSchedule() > -->

    <!-- < Else > -->
    <service
        android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService"
        android:exported="false" >
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>


    <service android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationActionService" />
    <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationActionHandlerReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="${applicationId}.firstAction" />
            <action android:name="${applicationId}.secondAction" />
        </intent-filter>
    </receiver>
</application>

@maleking, I have pasted the content of AndroidManifest.xml file of my project. Because I was unable to attach the .xml file.

For me, the issue was that although the app would come to the foreground on clicking on the app link (say from an SMS), the callback passed to Linking.addEventListener never got fired.

So what I ended up doing is this: add an AppState listener to check the url from Linking.getInitialURL, and if it is a valid URL, redirect accordingly.

componentDidMount() {
  // triggered when react native boots
  this._checkInitialUrl()

  AppState.addEventListener('change', this._handleAppStateChange)
  // never gets fired on Android, hence useless to me
  // Linking.addEventListener('url', url => console.log('url received: ', url))
}

componentWillUnmount() {
  AppState.removeEventListener('change', this._handleAppStateChange)
}

_handleAppStateChange = async (nextAppState) => {
  if (
    this.state.appState.match(/inactive|background/) &&
    nextAppState === 'active'
  ) {
    this._checkInitialUrl()
  }
  this.setState({ appState: nextAppState })
}

_checkInitialUrl = async () => {
  const url = await this._getInitialUrl()
  this._handleUrl(url)
}

_getInitialUrl = async () => {
  const url = await Linking.getInitialURL()
  return url
}

_handleUrl = (url) => {
  // write your url handling logic here, such as navigation
  console.log('received URL: ', url)
}

Same issue here on 59.8.

Does any one have a work around on this? I'm facing the same issue for Android devices.
And @glocore 's solution is not work for me. It will fire the _checkInitialUrl even if I don't click on any link

@tranthienhao I am also wondering this. Does anyone have any light to shed on this? Is this a bug, or does Linking.addEventListener('url', method_name). not work on Android?

@apamphilon try the below code.
componentDidMount(){
this._checkInitialUrl()
AppState.addEventListener('change', this._handleAppStateChange)
Linking.getInitialURL().then(async (url) => {
if (url) {
console.log('Initial url is: ' + url);
}
}).catch(err => console.error('An error occurred', err));
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange)
}

   _handleAppStateChange = async (nextAppState) => {
     if (
       this.state.appState.match(/inactive|background/) &&
       nextAppState === 'active'
     ) {
       this._checkInitialUrl()
     }
     this.setState({ appState: nextAppState })
   }

   _checkInitialUrl = async () => {
     const url = await this._getInitialUrl()
     this._handleUrl(url)
   }

   _getInitialUrl = async () => {
     const url = await Linking.getInitialURL()
     return url
   }

this is working if the app is in background state too.

@DalbirKaur Thanks for the suggestion. I did try and do it this way but found that if the deep link has already been navigated to and then you switch screens and put the app back into the background and then relaunch, the event listener will fire again and navigate to that screen again.

Any thoughts?

for me, the issue with using appState as a workaround is that on iOS Linking.addEventListener('url') will get the latest url sent to the app - this is helpful if the app is used to share URLs. So each time a URL is sent to the app, it receives the correct one. Using appState and getInitialURL you only ever get the first url the app was launched with.

Is there any update on getting Linking.addEventListener('url') working properly on Android?

I am also facing same issue Linking.addEventListener('url') returns the initial url only, or Linking.getInitialURL() returns null. Linking.removeEventListener('url') doesnt work, how can we remove the existing url and get the latest url.

Does anyone can solve keep getting the old url instead of latest url issue?

Also facing this issue. The event listener fails to fire if the app is not already open.

facing the same issue. it is working fine for iOS for me by the way.

Facing the same issue. Not working for iOS. Cannot get the url (always null) when the app is closed when the link happens.

React Native Environment Info:
System:
OS: macOS 10.14.6
CPU: (8) x64 Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
Memory: 353.30 MB / 16.00 GB
Shell: 5.3 - /bin/zsh
Binaries:
Node: 11.9.0 - /usr/local/bin/node
npm: 6.5.0 - /usr/local/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 13.1, DriverKit 19.0, macOS 10.15, tvOS 13.0, watchOS 6.0
IDEs:
Xcode: 11.1/11A1027 - /usr/bin/xcodebuild
npmPackages:
react: 16.8.3 => 16.8.3
react-native: ^0.59.9 => 0.59.9
npmGlobalPackages:
react-native-cli: 2.0.1

When the app is in background getinitialurl method returns null it is not working in android any one find the solution for it..even though i used the app state it is not working for me

These Listeners calls when app is in background.Below code worked for me when app is in background in android

componentDidMount() {
Linking.addEventListener('url', this._handleOpenURL);
},
componentWillUnmount() {
Linking.removeEventListener('url', this._handleOpenURL);
},
_handleOpenURL(event) {
console.log(event.url);
}

Not sure if others are having this problem, but for me the event listener doesn't work (Linking.addEventListener('url', link => ({ link }));)
Also, as has been mentioned, Linking.getInitialUrl only works on cold start, not from background.
Edit: I don't have the debugger running

Found that while running with the debugger in iOS simulator it will work through the event listener but not with Linking.getInitialURL().

Also, my team found that without the debugger it works fine.

Doing a trace, it correctly sets the bridge values/info but when calling the method getInitialURL, the bridge no longer holds those values.

It seems that there is a reset between both moments when the Debugger is present.

I hope this can be a hint to investigate the issue and for the solution.
I will post more info if I find something more.

@joaocac Found mostly the same on my first run through this, but now after using a combination of Linking.addEventListenerand Linking.getInitialURL() I only don't see a URL during cold-start. All other initiations work fine and I see the deep-linked URL. On RN 0.59, iOS.

If using firebase dynamic links then you can use onLink() event listener for latest url. Also make sure you call getInitialLink() when component mounts.

For those that are still facing the issue of not getting an event when a url is opened when the app is in background, I have a workaround for iOS.

in openURL in AppDelegate, add this line:
[[NSUserDefaults standardUserDefaults] setObject:url.absoluteString forKey:@"deepLinkURL"];

In your component in JS, add this:

Settings.watchKeys(["deepLinkURL"], () => {
      const url = Settings.get("deepLinkURL");
});

Make sure you add "RCTSettings" as a sub-pod in your podfile.

Pls try this.
componentDidMount() {
Linking.addEventListener("url", this.handleOpenURL);
this.handleDeepLinkingRequests();
}
handleDeepLinkingRequests = () => {
Linking.getInitialURL()
.then(url => {
if (url) {
this.handleOpenURL(url);
}
})
.catch(error => { // Error handling });
}
};

handleOpenURL = (url) => {
// your navigation logic goes here
}

Notes:
-> Linking.getInitialURL() method should only be called for the first time when the app is launched via app-swap
-> For subsequent app-swap calls, handleOpenURL() method will be called as it is configured with linking event listener.
-> remember to unsubscribe linking events in componentwillunmount()

after investigation, I found the solution, add this code in your AppDelegate.m

#import "RCTLinkingManager.h"
...
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(NSDictionary<NSString *, id> *)options {
  return [RCTLinkingManager application:application openURL:url options:options];
}

reference: https://v5.rnfirebase.io/docs/v5.x.x/links/ios

@joaocac Have you figured it out? I'm having the same problem!

Facing the same issue on android.

@devanshu23598

try to add this code below in you MainActivity.java

  import android.content.Intent;
  import android.net.Uri;
  import com.facebook.react.bridge.Arguments;
  import com.facebook.react.bridge.WritableMap;
  import com.facebook.react.modules.core.DeviceEventManagerModule;
  ...
  @Override
  public void onNewIntent(Intent intent) {
      if (intent.getData() != null) {
        Uri deepLinkURL = intent.getData();
        // note deeplink_identifier means the identity that you register in the manifest.
        if (deepLinkURL.toString().contains("deeplink_identifier")) {
            // Create map for params
            WritableMap event = Arguments.createMap();
            // Put data to map
            event.putString("url", deepLinkURL.toString());
            // Get EventEmitter from context and send event thanks to it
            getReactInstanceManager().getCurrentReactContext()
                    .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                    .emit("url", event);
        } else {
           // to handle other deeplink that not related to the defined deeplink identifier such as notification
           setIntent(intent);
        }
      } 
  }

@RZulfikri Thank you for your response!!
I added the code in MainActivity.java and it works fine when running through terminal. I want to open the app through web browser. That not work.

after investigation, I found the solution, add this code in your AppDelegate.m

#import "RCTLinkingManager.h"
...
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(NSDictionary<NSString *, id> *)options {
  return [RCTLinkingManager application:application openURL:url options:options];
}

reference: https://v5.rnfirebase.io/docs/v5.x.x/links/ios

Thanks, this worked on RN 0.62.2. Can someone explain what this code bit did?

I have same issue with react native 0.61

Pls try this.
componentDidMount() {
Linking.addEventListener("url", this.handleOpenURL);
this.handleDeepLinkingRequests();
}
handleDeepLinkingRequests = () => {
Linking.getInitialURL()
.then(url => {
if (url) {
this.handleOpenURL(url);
}
})
.catch(error => { // Error handling });
}
};

handleOpenURL = (url) => {
// your navigation logic goes here
}

Notes:
-> Linking.getInitialURL() method should only be called for the first time when the app is launched via app-swap
-> For subsequent app-swap calls, handleOpenURL() method will be called as it is configured with linking event listener.
-> remember to unsubscribe linking events in componentwillunmount()

However this didn't work for me.

Facing same issue 0.62.0

Same issue, seems like Linking.getInitalURL() works if the app is not yet launched, and Linking.addEventListener('url' callback) works if the app is in foreground but neither seem to work for apps in background state.

// Add Listener to catch a state


componentDidMount() {
   Linking.addEventListener('url', this._handleOpenURL);
},
componentWillUnmount() {
   Linking.removeEventListener('url', this._handleOpenURL);
},
_handleOpenURL(event) {
   console.log(event.url);
}

Is there an update on this issue ? Deep linking is an important feature, and this issue makes it pretty much unusable

So, apparently, it works just fine if you turn off the debugger

Was this page helpful?
0 / 5 - 0 ratings