react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Linking.getInitialURL() for android

Open sajaddp opened this issue 5 years ago • 85 comments

Hi, On Android Linking.getInitialURL() has a problem and not return anything. More precisely, none of the following logs are executed.

  Linking.getInitialURL().then((url) => {
      console.log('Initial url is: ' + url);
  }).catch(err => console.error('An error occurred', err));

but when i change https://github.com/facebook/react-native/blob/master/Libraries/Linking/Linking.js#L86-L92 to:

  getInitialURL(): Promise<?string> {
    return NativeLinking.getInitialURL();
  }

the problem was fixed.

React Native version:

System:
    OS: Linux 4.18 Ubuntu 18.04.2 LTS (Bionic Beaver)
    CPU: (8) x64 Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
    Memory: 582.26 MB / 15.50 GB
    Shell: 4.4.20 - /bin/bash
  Binaries:
    Node: 10.16.0 - /usr/bin/node
    Yarn: 1.17.3 - /usr/bin/yarn
    npm: 6.9.0 - /usr/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  SDKs:
    Android SDK:
      API Levels: 28
      Build Tools: 28.0.3, 29.0.0, 29.0.1
      System Images: android-28 | Intel x86 Atom, android-28 | Intel x86 Atom_64, android-28 | Google APIs Intel x86 Atom, android-28 | Google APIs Intel x86 Atom_64, android-28 | Google Play Intel x86 Atom, android-28 | Google Play Intel x86 Atom_64, android-29 | Google APIs Intel x86 Atom
      Android NDK: 20.0.5594570
  npmPackages:
    react: 16.8.6 => 16.8.6 
    react-native: 0.60.3 => 0.60.3 
  npmGlobalPackages:
    react-native-cli: 2.0.1

sajaddp avatar Jul 16 '19 13:07 sajaddp

I can confirm that on Android, if the app is closed, InteractionManager.runAfterInteractions, will not work and then the getInitialURL will not return anything.

But if the app was opened, everything works just fine.

I removed the InteractionManager.runAfterInteractions and the deep linking on Android, when the app is closed, works again

Thanks @sajaddp

tgensol avatar Sep 06 '19 09:09 tgensol

I fixed by that:

var NativeLinking=require("./node_modules/react-native/Libraries/Linking/NativeLinking").default;
NativeLinking.getInitialURL().then((url) => {
      console.log('Initial url is: ' + url);
  }).catch(err => console.error('An error occurred', err));

i7soft avatar Sep 26 '19 13:09 i7soft

I'm also facing this issue on RN v0.59.8. I'm trying to use aws-amplify library and they make use of Linking.getInitialUrl() to listen to the Deeplink URL but it is always returning null. Can anyone suggest some solutions that I can try?

shubham234 avatar Nov 16 '19 14:11 shubham234

Same here iOS is working great but Android fails.

System:
    OS: macOS 10.15.2
    CPU: (8) x64 Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
    Memory: 68.70 MB / 16.00 GB
Packages:
    react: 16.9.0 => 16.9.0
    react-native: 0.61.2 => 0.61.2
    react-native-cli: 2.0.1

joaocac avatar Jan 28 '20 09:01 joaocac

We are seeing what we think is the same issue—works great on iOS, fails on Android cold starts. In particular, it fails on Android 10. It works on an old Android 7 phone I have.

mileszs avatar Jan 28 '20 20:01 mileszs

Any update on this? Or workaround?

Stephen2 avatar Feb 19 '20 00:02 Stephen2

Interestingly, getInitialUrl works on Google Pixel XL2 but doesn't work on Samsung S9 - both running Android 10.

I even changed code to: import NativeLinking from "react-native/Libraries/Linking/NativeLinking"; NativeLinking.getInitialURL().then(handleDeepLinking); (^^ code is cut down, I actually went as far as to put url into state and display in a <Text> for debug build, but nothing)

But to no avail... Samsung S9 refuses to call the handleDeepLinking function from cold start :( Warm start is fine, and anyway warm start is handled by Linking.addEventListener("url", ev => {

Stephen2 avatar Feb 19 '20 16:02 Stephen2

"react": "16.9.0",
"react-native": "0.61.4",
"expo": "^36.0.0",

Stephen2 avatar Feb 19 '20 16:02 Stephen2

UPDATE:

  • forking react-native and reverting https://github.com/facebook/react-native/pull/24748 did NOT solve the issue
  • I downgraded Expo SDK from 36 to 35, which moved react-native from 0.61.4 back to 0.59.8 which did solve the issue

And please note the differing behaviour on different Android devices during my testing. This was a consistent failure on Samsung, but consistently fine on Google Pixel

I will attempt to do a git log --follow on a few files between these 2 react-native versions, to get more info, but at this point I don't know which files/folders are most likely culprits.

Stephen2 avatar Feb 24 '20 20:02 Stephen2

I can confirm that I have this issue since I've updated from 0.59.* to 0.61.*

aurasphere avatar Feb 25 '20 13:02 aurasphere

Is there any update on this issue? I'm experiencing the same. Not being able to utilize deep linking is a huge miss right now.

phillipkey avatar Mar 06 '20 19:03 phillipkey

Is there any update? The bug still continues

bsonmez avatar Mar 17 '20 11:03 bsonmez

Finally am not dumb.... Is there any update? The bug still continues

openwell avatar Mar 17 '20 17:03 openwell

The same issue. Linking.getInitialURL works fine on ios, but on android(emulator SDK 28) this method seems not even to be called.

"react-native": "0.61.5",
"react-native-navigation": "^4.5.3",

ryzhak avatar Mar 20 '20 13:03 ryzhak

Same here on 0.61.5

danieltrizzuto avatar Apr 01 '20 20:04 danieltrizzuto

Any progress on this? Seems mind boggling to me that deeplinking is just completely broken in RN for Android?

xanderdeseyn avatar Apr 05 '20 10:04 xanderdeseyn

Any progress on this? Seems mind boggling to me that deeplinking is just completely broken in RN for Android?

It’s not completely broken for me. It works for an already started app, but not for a newly started one.

norbertschuler avatar Apr 05 '20 12:04 norbertschuler

Any progress on this? Seems mind boggling to me that deeplinking is just completely broken in RN for Android?

It’s not completely broken for me. It works for an already started app, but not for a newly started one.

Well I'd argue that is almost completely broken then. Deeplinking is usually used when the app is not open.

xanderdeseyn avatar Apr 07 '20 17:04 xanderdeseyn

The culprit seems to be this line here https://github.com/facebook/react-native/blob/c0d8c1db9077d08c3832b23e959bf70ad204307d/Libraries/Linking/Linking.js#L88

InteractionManager.runAfterInteractions takes too long and Linking.getInitialURL never resolves the desired effect!

After removing it it worked 100% of the times, so @Stephen2 workaround actually fixes it by the time being...

import NativeLinking from 'react-native/Libraries/Linking/NativeLinking';
NativeLinking.getInitialURL().then(url => {
      handleDeepLink({ url });
 });

rsmelo92 avatar Apr 10 '20 21:04 rsmelo92

import NativeLinking from 'react-native/Libraries/Linking/NativeLinking';
NativeLinking.getInitialURL().then(url => {
      handleDeepLink({ url });
 });

I've tried that earlier

But to no avail... Samsung S9 refuses to call the handleDeepLinking function from cold start

Stephen2 avatar Apr 10 '20 22:04 Stephen2

I'm not using Expo SDK, maybe that's what different. Here it works!

rsmelo92 avatar Apr 10 '20 22:04 rsmelo92

Jest started complaining and now changed solution to

export function getInitialURLOverride() {
  if (Platform.OS === 'android') {
    const NativeLinking = require('react-native/Libraries/Linking/NativeLinking').default;
    NativeLinking.getInitialURL().then(url => url && handleDeepLink({ url }));
  } else {
    Linking.getInitialURL().then(url => url && handleDeepLink({ url }));
  }
}

rsmelo92 avatar Apr 10 '20 22:04 rsmelo92

For me and team it was device specific. Google Pixel XL2 worked, Samsung S9 didn't.

What devices are you testing with, may I ask?

Stephen2 avatar Apr 10 '20 23:04 Stephen2

~~I'm experiencing this in both Android and iOS as a result of the websocket implementations crashing due to a null base url.~~ Whew. This issue it not the cause of that websocket problem. However, I kept running into the getInitialURL problem while debugging the websocket problem (which I caused) on Android and had to switch to iOS to consistently reproduce.

ssangervasi avatar Apr 10 '20 23:04 ssangervasi

I'm experiencing the same problem. myapp://profile is not resolved after app cold start myapp://profile is resolved when the app is launched and in background state (miminized)

@kromsik Did you try @rsmelo92 fix? It works for me in both scenarios.

VictorAtPL avatar Apr 16 '20 11:04 VictorAtPL

Same here, i having an issue on Linking.getInitialURL() that are not returning the correct url. I tried the fix above but it's nothing happen.

esxquillon avatar Apr 21 '20 19:04 esxquillon

Does this happen only on link open or on any app start?

Bardiamist avatar May 13 '20 05:05 Bardiamist

I was able to replicate this issue consistently. The issue comes up because when our application is opened from cold, we have a spinner that is animated using Animated.timing while it loads other information (API calls and yes, the Linking.getInitialURL)

It looks like any animation will queue an interaction with the InteractionManager. The InteractionManager, in turn, will not look up the initialURL unless the queue is completely empty.

Offending code:

  spinIt() {
    this.spinValue.setValue(0);
    Animated.timing(this.spinValue, {
      toValue: 1,
      duration: 800,
      easing: Easing.Out,
      useNativeDriver: false,
    }).start(() => this.spinIt());
  }

Fixed code:

  spinIt() {
    this.spinValue.setValue(0);
    Animated.timing(this.spinValue, {
      toValue: 1,
      duration: 800,
      easing: Easing.Out,
      useNativeDriver: false,
    }).start(() => setImmediate(this.spinIt.bind(this)));
  }

I also tried replacing with Animated.loop, but I found that I had the same issue. Only solve was to schedule the next loop using setImmediate.

EDIT: Animated.loop works, but the child animation must be set to isInteraction: false so that it does not block.


    Animated.loop(Animated.timing(this.spinValue, {
      toValue: 1,
      duration: 800,
      easing: Easing.Out,
      useNativeDriver: false,
      isInteraction: false
    })).start();

TL;DR: Animations are usually interpreted as interactions, which block the getInitialUrl call. It's possible the issue is with your animations.

crisvergara avatar May 26 '20 21:05 crisvergara

I think there are probably 2 different issues/bugs here:

  1. The promise returned by Linking.getInitialURL() seems not to resolve for some people (see example of @crisvergara above)
  2. The promise returned by Linking.getInitialURL() does resolve and either returns null or a default Expo URL starting with https://expo.io (only if using the managed Expo workflow)

I think it probably makes sense to separate these 2 issues or at least declare which of the 2 people are seeing. For me, I am seeing number 2 above.

I did some testing around this and have come to similar conclusions to @Stephen2:

  • It does not seem related to a particular Android version, but actually depends on timing. That's why it seems to happen on some phones rather than others I think. In addition to it only happening on certain phones, I found that you can get inconsistent results on the same phone if you repeat enough.
  • It is resolved by moving back from React Native 0.61 to React Native 0.59. Unfortunately, for those using Expo, this means going back to the Expo 35 SDK, which Apple is no longer accepting submissions from due to usage of deprecated UIWebView (see https://github.com/expo/expo/issues/5497)
  • It is not related to Expo and can be reproduced by a trivial example in latest React Native after running react-native init and making some changes to App.js and AndroidManifest.xml (to register intent to view certain URLs). I have pushed this sample app at https://github.com/stellaai/rndeeplink

Since the bug seems timing dependent and having put tracing debug statements throughout the native code, my hunch is that this relates to whether or not the React bridge is ready when the native code wants to set the initial URL by sending a message to the JS app via EventEmitter (if my reading of the code is correct and this is how it is set).

A workaround/hack (if using vanilla React Native or willing to eject from Expo managed workflow) might be to override the getLaunchOptions method in your own ReactActivityDelegate subclass to explicitly get the URI from the Intent and pass it through as a prop to the root React component rather than relying on Linking.getInitialUrl(). See https://medium.com/@anutoshdatta/pass-initial-properties-to-react-native-from-java-or-objective-c-code-12880ba6198e for some details on this.

I have put a video of these inconsistent results up here: https://github.com/stellaai/rndeeplink/blob/master/deeplinkbug.mp4 -- note that pressing the the back button on Android destroys the activity, so this is showing a cold start every time.

Have also logged a bug in Expo prior to determining that this was a bug with RN upstream with some more logs, etc.: https://github.com/expo/expo/issues/8408

jtbeach avatar May 27 '20 05:05 jtbeach