3

I've had this problem for like 2 weeks. I used Wix's Navigation for navigating around the app. I followed this tutorial for implementing the deeplink/universal link.

I have a base class called BaseScreen where I keep all the deeplink handler like in the tutorial. This BaseScreen would looks like this:

componentDidMount(){
    // this handles the case where the app is closed and is launched via Universal Linking.
    Linking.getInitialURL()
        .then((url) => {
          if (url) {
            // Alert.alert('GET INIT URL','initial url  ' + url)
            this.resetStackToProperRoute(url)
          }
        })
        .catch((e) => {})

   // This listener handles the case where the app is woken up from the Universal or Deep Linking
   Linking.addEventListener('url', this.appWokeUp);
  }

  componentWillUnmount(){
    // Remove the listener
    Linking.removeEventListener('url', this.appWokeUp);
  }

  appWokeUp = (event) => {
    // this handles the use case where the app is running in the background and is activated by the listener...
    // Alert.alert('Linking Listener','url  ' + event.url)
    this.resetStackToProperRoute(event.url)
  }

  resetStackToProperRoute = (url) => {
    // grab the trailing portion of the url so we can use that data to fetch proper information from the server
    let trailing = url.slice(url.lastIndexOf('=') + 1, url.length)
    // go to the desired screen with the trailing token grabbed from the url
    this.props.navigator.resetTo({
      screen: 'NewPassword',
      overrideBackPress: true,
      passProps: {
        token: trailing
      },
      animated: true,
      animationType: 'fade',
      navigatorStyle: {
      navBarHidden: true,
  }
})
  }

When the app launch, it'll show the screen LoginScreen which extends the BaseScreen above. After killing the app, click the url from the mail, the app launches LoginScreen first, then it'll redirect to the screen NewPassword, and after everything has done, I'll redirect back to LoginScreen by:

this.props.navigator.resetTo({
  screen: 'LoginScreen',
  animated: true,
  overrideBackPress: true,
  animationType: 'fade',
  navigatorStyle: {
    navBarHidden: true,
  }
})

But the Linking.getInitialURL() of the LoginScreen still receive the old url, so it'll redirect to NewPassword again, and it's a loop.

I've also tried to pass: passProps: {} option when resetTo the LoginScreen but no luck.

I guess the only way to fix it is to clear the initialUrl manually after everything's done in NewPassword screen. The listener for the BaseScreen should be there because if I don't kill the app (just minimize it), the listener should be running to navigate to NewPassword.

Wix's navigation has a doc for Deeplink, I tried putting method onNavigatorEvent(event) into the BaseScreen but it doesn't get called. I don't know if I miss something.

Thank you for your time. Any idea would be appreciated

Tran Hoai Nam
  • 1,273
  • 2
  • 18
  • 35
  • add android:launchMode="singleTask" to your main .MainActivity in AndroidManifest https://stackoverflow.com/questions/45752822/linking-getinitialurl-constantly-being-executed-and-app-being-duplicated?noredirect=1&lq=1 – Casper Apr 30 '20 at 07:21

2 Answers2

7

Linking.getInitialURL() gives us the same Url when we come back to the same page again, to Overcome this we can do a simple condition of not to call the DeepLink function. Something like...

Step 1: First init a dummyDeepLinkedUrl String .

var dummyDeepLinkedUrl;

Step 2: Check for the condition like, if deeplinkUrl is coming from Linking.getInitialURL() and deeplinkUrl is not equal to the dummyDeepLinkedUrl .

if (url && url != dummyDeepLinkedUrl) {}

Step 3: If not same call the Deeplink Function and assign the deeplinkUrl to dummyDeepLinkedUrl.

    this.navigateToRespectivePage(url);
    dummyDeepLinkedUrl = url;

Finally this will look like :

Linking.getInitialURL().then(url => {
      if (url && url != dummyDeepLinkedUrl) {
        this.navigateToRespectivePage(url);
        dummyDeepLinkedUrl = url;
      }
    });
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
sommesh
  • 923
  • 9
  • 19
0

There are two ways to handle URLs that open your app.

  1. If the app is already open, the app is foregrounded and a Linking event is fired You can handle these events with Linking.addEventListener(url, callback).

  2. If the app is not already open, it is opened and the url is passed in as the initialURL You can handle these events with Linking.getInitialURL(url) -- it returns a Promise that resolves to the url, if there is one.

You can read more detail Here is the example

export default class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      initialised: false
    }
  }

  componentDidMount() {
    AppState.addEventListener('change', this._handleAppStateChange);
    Linking.addEventListener('url', event => {
       console.log('deep link from background', event.url)
    })
  }

  _handleAppStateChange = async (nextAppState) => {
    const url = await Linking.getInitialURL();
    if (url !== null && !this.state.initialised) {
      this.setState({ initialised: true })
      console.log('deep link from init app', url)
    }
  }

  componentWillUnmount() {
    AppState.removeEventListener('change', this._handleAppStateChange);
    Linking.removeEventListener('url')
  }
}
Heo Đất Hades
  • 1,573
  • 18
  • 14