Search code examples
react-nativereact-navigationdeep-linkingdeeplink

Deeplink with nested DrawerNavigator, TabNavigator and StackNavigator


I'm trying to configure deeplinks for my React Native app, following the official documentation, but I haven't been able to make it work. I mean, the app does open if I run adb shell am start -W -a android.intent.action.VIEW -d “crf://" packageName or xcrun simctl openurl booted crf:// but I haven't been able to open a specific screen; it always launches to the app home screen. I'm almost sure it has to do with the nested navigators, since I have a TabbarNavigator inside a DrawerNavigator and a StackNavigator inside all of that. I followed the instructions regarding nested navigators in the documentation and also this post, amongst other things, but no luck. I want to go to the EventHomeScreen, PersonDetailScreen and ProgramSessionDetail

Here is my code:

NavigatorRouter

const mainNavigator =  createStackNavigator(
  { 
    [Constants.APP_HOME_SCENE_KEY]:{
      screen: AppHomeScreen,
      navigationOptions: {
        title: 'App Home',
        showBack: false,
        showSearch: false,
      },
      path: ''
    },
    [Constants.EVENT_HOME_SCENE_KEY]:{
      screen:  navPropMapper()(EventHomeScreen),
      navigationOptions:{
        title: 'Home',
        path: 'event'
      }
    },
    [Constants.ATTENDEE_DETAIL_SCENE_KEY]:{
      screen: navPropMapper()(PersonDetailScreen),
      navigationOptions:{
        title: 'Attendee Detail', 
        // path: 'person/:user'
        path: 'person'
      }
    },
    [Constants.PROGRAM_DETAIL_SCENE_KEY]:{
      screen:  navPropMapper()(ProgramSessionDetail),
      navigationOptions:{
        title: 'Program',
        path: 'program/:idLecture'
      }
    }
  },
  { 
    initialRouteName: `${Constants.APP_HOME_SCENE_KEY}`, 
    defaultNavigationOptions: {
      header: props => <NavBar {...props} />,
      gesturesEnabled: false,
      showBack: true,
      showHome: false,
      showSearch: true,
      showWebExplorer: false
    }
  });

const tabbarNavigator = createBottomTabNavigator({
    Main: {
      screen: mainNavigator,
      path: 'tabs'
    },
  }, {
    tabBarComponent: Tabbar,
  });

const drawerNavigator = createDrawerNavigator({
    Drawer: {
      screen: tabbarNavigator,
      navigationOptions:{
        drawerLockMode: 'locked-closed',
      },
      path: 'drawer'
    },
  }, {
    contentComponent: ({ props }) => <DrawerContainer {...props}/>,
    drawerPosition: 'right',
    unmountInactiveRoutes: true,
    defaultNavigationOptions: {
      header: null,
    }
  });

const wraperNavigator = createStackNavigator({
  MainComponents: {
    screen: drawerNavigator,
    path: 'main'
  },
  [Constants.MODAL_FEEDBACK]:{
    screen:  navPropMapper()(Modal),
    navigationOptions:{
      title: 'Feedback',
    }
  },
  [Constants.MODAL_LOGIN]:{
    screen:  navPropMapper()(ModalLogin),
    navigationOptions:{
      title: 'Login',
    }
  },
}, {
  mode: 'modal',
  cardStyle:{
    backgroundColor: 'transparent',
    opacity: 1,
  },
  cardShadowEnabled: true,
  cardOverlayEnabled: true,
  transparentCard: true,
  defaultNavigationOptions: {
    header: null,
    gesturesEnabled: false,
  },
}); 

export default createAppContainer(wraperNavigator)

App.js

class App extends Component {

  render () {
    return (
      <Provider store={store}>
        <RootContainer />
      </Provider>
    )
  }
}

export default App

RootContainer

class CRrootContainer extends Component {

  render () {
    return (
      <View style={styles.applicationView}>
        <NavigationRouter uriPrefix={'crf://'}/>
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    navState: state.navigation,
  }
}

// wraps redux integration
const mapDispatchToProps = (dispatch) => ({
  startup: () => dispatch(StartupActions.startup()),
})

export default connect(mapStateToProps, mapDispatchToProps)(rootContainer)

iOS Uri and Scheme

AppDelegate.m

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  return [RCTLinkingManager application:application openURL:url
                      sourceApplication:sourceApplication annotation:annotation];
}

URL Types:

URL types

Android Scheme:

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="packageName">

    <application
        android:name=".MainApplication"
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:theme="@style/AppTheme">

        <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="crf"/>
            </intent-filter>
        </activity>

    </application>

</manifest>

The commands I'm trying are:

xcrun simctl openurl booted crf://main/drawer/tabs/event for iOS.

and

adb shell am start -W -a android.intent.action.VIEW -d “crf://main/drawer/tabs/event" packageName for Android (packageName is the name of my app package`

My react-navigation version is 3.6.1 and react-native: 0.59.9.


Solution

  • So it ended up beeing a rookie mistake, putting the path: keyword on the wrong place; it needs to go outside the navigationOptions, not inside. Also, path value on drawer navigator and tabbar navigator should be empty.

    const mainNavigator =  createStackNavigator(
    { 
        [Constants.APP_HOME_SCENE_KEY]:{
          screen: AppHomeScreen,
          navigationOptions: {
            title: 'App Home',
            showBack: false,
            showSearch: false,
          },
          path: ''
        },
        [Constants.EVENT_HOME_SCENE_KEY]:{
          screen:  navPropMapper()(EventHomeScreen),
          navigationOptions:{
            title: 'Home',
          },
          path: 'event'
        },
        [Constants.ATTENDEE_DETAIL_SCENE_KEY]:{
          screen: navPropMapper()(PersonDetailScreen),
          navigationOptions:{
            title: 'Attendee Detail'
          },
          path: 'person'
        },
        [Constants.PROGRAM_DETAIL_SCENE_KEY]:{
          screen:  navPropMapper()(ProgramSessionDetail),
          navigationOptions:{
            title: 'Program'
          },
          path: 'program/:idLecture'
        }
      },
      { 
        initialRouteName: `${Constants.APP_HOME_SCENE_KEY}`, 
        defaultNavigationOptions: {
          header: props => <NavBar {...props} />,
          gesturesEnabled: false,
          showBack: true,
          showHome: false,
          showSearch: true,
          showWebExplorer: false
        }
      });
    
    const tabbarNavigator = createBottomTabNavigator({
        Main: {
          screen: mainNavigator,
          path: ''
        },
      }, {
        tabBarComponent: Tabbar,
      });
    
    const drawerNavigator = createDrawerNavigator({
        Drawer: {
          screen: tabbarNavigator,
          navigationOptions:{
            drawerLockMode: 'locked-closed',
          },
          path: ''
        },
      }, {
        contentComponent: ({ props }) => <DrawerContainer {...props}/>,
        drawerPosition: 'right',
        unmountInactiveRoutes: true,
        defaultNavigationOptions: {
          header: null,
        }
      });
    
    const wraperNavigator = createStackNavigator({
      MainComponents: {
        screen: drawerNavigator,
        path: ''
      },
      [Constants.MODAL_FEEDBACK]:{
        screen:  navPropMapper()(Modal),
        navigationOptions:{
          title: 'Feedback',
        }
      },
      [Constants.MODAL_LOGIN]:{
        screen:  navPropMapper()(ModalLogin),
        navigationOptions:{
          title: 'Login',
        }
      },
    }, {
      mode: 'modal',
      cardStyle:{
        backgroundColor: 'transparent',
        opacity: 1,
      },
      cardShadowEnabled: true,
      cardOverlayEnabled: true,
      transparentCard: true,
      defaultNavigationOptions: {
        header: null,
        gesturesEnabled: false,
      },
    }); 
    
    export default createAppContainer(wraperNavigator)