i’m looking for advice on how to implement deeplink in a react-native app. I have 3 screens – PaymentScreen.jsx
, HomeScreen.jsx
and OBPaymentResults.jsx
. I also have Navigator.jsx
.
I’m having a hard time making deeplink work and need your guidance. The process i intend to have is this:
- User pushes a button on the
PaymentScreen
, which opens web browser with website X. The website recieves the request with callback parameter (my deeplink, likemypay://ob_payment_results/12
) - User provides some input in the browser window
- the website X upon finishing its job, shall call the deeplink to return the focus to the app.
The problem i’m faced is that:
- The web site X won’t close the webbroser and shift focus back to the app. Meaning calling the deeplink didn’t work out.
- Within the
HomeScreen.jsx
i also tried to call the deeplink using a button. It won’t open the correct screenOBPaymentResults.jsx
, instead staying on theHomeScreen.jsx
- When i used adb it just open the
HomeScreen.jsx
.
My AndroidManifest.xml
has the following structure:
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme" android:usesCleartextTraffic="true">
<meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="barcode"/>
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="mypay" android:host="ob_payment_results"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="mypay" android:host="home"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" android:host="whimsysketchbook.com" android:pathPrefix="/"/>
</intent-filter>
</activity>
</application>
My TabNavigator.jsx
has the following structure:
/* eslint-disable react-native/no-inline-styles */
/* eslint-disable no-alert */
/* eslint-disable react/no-unstable-nested-components */
// TabNavigator.js
import React from "react";
import {
Button,
View,
Image,
FlatList,
Text,
TouchableOpacity,
Linking,
LogBox
} from "react-native";
import { useState, useEffect } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { ScannerScreen } from "./ScannerScreen";
import { openExternalLink } from "./utils";
import DetailScreen from "./DetailScreen";
import HomeScreen from "./HomeScreen";
import PayScreenSummary from "./PayScreenSummary";
import PaymentScreenPaymentSource from "./PaymentScreenPaymentSource";
import OBPaymentResults from "./OBPaymentResults";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
/**
* Linking Configuration
*/
const linking = {
// Prefixes accepted by the navigation container, should match the added schemes
prefixes: ["mypay://"],
// Route config to map uri paths to screens
config: {
// Initial route name to be added to the stack before any further navigation,
// should match one of the available screens
initialRouteName: 'Home',
screens: {
// myapp://home -> HomeScreen
OBPaymentResults: {
path: 'ob_payment_results/:consent_id',
parse: {
consent_id: Number,
},
},
// myapp://details/1 -> DetailsScreen with param id: 1
Home: {
path: 'home'
},
},
},
async getInitialURL() {
// Check if app was opened from a deep link
const url = await Linking.getInitialURL();
if (url != null) {
return url;
}
// Check if there is an initial firebase notification
const message = await messaging().getInitialNotification();
// Get the `url` property from the notification which corresponds to a screen
// This property needs to be set on the notification payload when sending it
return message?.data?.url;
}
};
/**
* Stack Navigator
*/
const Stack = createNativeStackNavigator();
const TabNavigator = () => {
return (
<Stack.Navigator initialRouteName="Home" linking={linking}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen
name="Scanner"
component={ScannerScreen}
options={{
headerTitle: "Scanner",
}}
/>
<Stack.Screen name="PayScreenSummary" component={PayScreenSummary} />
<Stack.Screen
name="PaymentScreenPaymentSource"
component={PaymentScreenPaymentSource}
/>
<Stack.Screen name="Detail" component={DetailScreen} />
<Stack.Screen
name="OBPaymentResults"
component={OBPaymentResults}
options={({ route }) => ({ consent_id: route.params.consent_id })}
/>
</Stack.Navigator>
);
};
export default TabNavigator;
I also have HomeScreen
, which keeps opening whenever i call the deeplink, instead of OBPaymentResults
.
My target screen OBPaymentResults
is super minimalistic:
import React, { useState, useEffect } from 'react';
import {
Button,
StyleSheet,
Text,
View,
TextInput,
Linking,
} from "react-native";
const OBPaymentResults = ({route, navigation}) => {
console.log(route);
console.log(route.params.consent_id);
return (
<View style={styles.container}>
<Button title="Back" onPress={() => navigation.goBack()} />
<TextInput
value=''
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default OBPaymentResults;
I have used the Android Debug Bridge with the activity manager (am) tool to test that the intent filter URIs I specified for deep linking resolves to the correct app activity.
➜ ~ adb shell am start -W -a android.intent.action.VIEW -d "mypay://ob_payment_results/12" com.mypay
AND
➜ ~ adb shell am start -W -a android.intent.action.VIEW -d "mypay://ob_payment_results/12"
Give proper output:
Starting: Intent { act=android.intent.action.VIEW dat=mypay://ob_payment_results/... pkg=com.mypay }
Status: ok
LaunchState: COLD
Activity: com.mypay/.MainActivity
TotalTime: 595
WaitTime: 600
Complete
So these commends open the app, but on the Home screen, not on the OBPaymentResults which is what i expect.
Finally, here is the output of adb shell pm get-app-links
com.mypay:
ID: efc-f825-4d37-91be-af1750
Signatures: [FA:C6:17:45:DC:09:03:78:6F:6F:89:9B:83:32:66:75:91:03:3B:9C]
Domain verification state:
whimsysketchbook.com: 1024