React Native Deeplink

App 一般都支持类似 coolflight://list 这样的链接,可以直接打开 app 并打开列表,这个就是 deeplink。

这个需要对 native 代码做一些修改,可以参考这里的修改,ios 和 android 都有写。这里有一个需要注意的是,对于 android 有一个配置是

    <data android:scheme="mychat" android:host="mychat" />

这个里面配置 host 的话,后面使用的时候就需要类似 mychat://mychat/list 这样的方式了,就是多了一层 mychat。这样也会导致 ios 和 android 的链接不统一,我查了文档也没有查到没有设置 host 会有什么问题,我就去掉了,去掉之后,ios 和 android 的链接就统一了。都是 mychat://list

另外 android 还有一个需要注意的地方是,activity 的 launchmod 需要设置为 singleTask 要不会导致每次通过 deeplink 打开 app 都会新建一个,导致你有多个 js 在后台跑。

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

在 js 里面可以使用 Linking.openURL(url).catch(err => console.error('An error occurred', err)) 打开一个 deeplink ,可以是别的 app 的,也可以是自己的。

然后就是在 js 里面处理对应的 deeplink 了。

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

  Linking.addEventListener('url', this._handleOpenURL);
}

componentWillUnmount() {
  Linking.removeEventListener('url', this._handleOpenURL);
}

componentDidMount 里面,通过 Linking.getInitialURL() 可以得到 app 冷启动的时候拿到的 link。通过 Linking.addEventListener('url', callback) 可以拿到热启动 app 拿到的 link,分别处理或者统一处理都可以,看业务需求。

我们用的是 React Navigation,他支持可以直接给 screen 设置 path,然后和 deeplink 匹配跳转。

const SimpleApp = createStackNavigator({
  Home: { screen: HomeScreen },
  Chat: {
    screen: ChatScreen,
    path: 'chat/:user',
  },
});


const prefix = 'mychat://'; // 这里我们上面提到的统一了,所以不用区分 ios 和 android
const MainApp = () => <SimpleApp uriPrefix={prefix} />;

这样只需要定义一个 uriPrefix 就可以了。

我们为了把 navigation 和 redux 结合,自定义了 navigation 的 navigation 属性,就不允许这么弄了。需要自己处理。

主要思路是通过 SimpleApp.router.getActionForPathAndParams(path, params) 得到 action,然后 dipatch 这个 action 就可以了。

那个 chat/:user 可以匹配到 mychat://chat/Jim 这样的 deeplink,然后那个 user: Jim 会以 param 的方式给到 screen,通过 param.user 可以访问到。

path 的格式支持的是这个 path-to-regexp 支持的格式,可以自定义表达式,具体可以参考那个文档。

path 支持使用 ? 来表示一个字段是可选的,例如 mychat://chat/:user? 表示会匹配到 mychat://chatmychat://chat/Jimmychat://chat/:user?/:msg? 这样的,可以匹配 mychat://chat mychat://chat/Jim mychat://chat/Jim/hey 但是不能匹配 mychat://chat//hey 。默认匹配的是 ([^\\/]+) 可以通过自定义表达式支持。

  path: 'chat/:user([^\\/]*)?/:msg?'