Expo deep linking in React Native Web - react-native

Trying to make deep linking for React Native Web work. Browsed many articles and finally found a solution proposed for the exact problem faced in this link whch doesn't work for me.
Expo documentation says use
const prefix = Linking.makeUrl('/')
and set the prefix in the AppNavigator.
Wondering how can I point to my react native web app when use clicks a link. The problem is Linking.makeUrl('/') returns exp://localhost:19006/ on my laptop. As browser wouldn't recognize exp as scheme is unable to open my URL.
I tried setting the scheme to https in app.json but still Linking.makeUrl('/') returns the scheme as expo.
In my app when user clicks on static html page which I load in the app I want to route back to my app. As I am unable to get right scheme for React Native Web using Expo unable to proceed further.
Thank you.
Attaching the code provided in the link.
import React from 'react'
import { createSwitchNavigator } from '#react-navigation/core'
import { createBrowserApp } from "#react-navigation/web"
import { Linking } from 'expo'
const prefix = Linking.makeUrl('/')
const AppNavigator = createBrowserApp(createSwitchNavigator({
Landing: {
screen: LandingScreen,
path: '/'
},
Login: {
screen: LoginScreen,
path: 'login'
}
}, {
initialRouteName: 'Landing',
backBehavior: 'history'
}))
export default () => <AppNavigator uriPrefix={prefix} />

Try :
const prefix = Linking.createURL('/');

Related

How to open React Native Expo app from Mail?

When I send an email I want to, when clicking on the button Reset, to open an Expo app.
I have created deep link in my app.
import * as Linking from 'expo-linking';
const prefix = Linking.createURL('/');
const linking = {
prefixes: [prefix],
config: {
screens: {
AccountCreated: 'account-created',
ResetPassword: 'reset-password'
}
}
}
and linked it to the navigation
<NavigationContainer linking={linking}>
...
</NavigationContainer>
When I send the mail I use the link exp://192.168.0.15:19000/--/reset-password to open the app and ResetPassword page.
<p>{{URL}}</p>
Reset
However, it does not open the app. What could be a solution?

How to use "promptAsync" from expo-auth-session, old library used in video tutorial was deprecated

I have been following the tinder 2.0 react native tutorial https://youtu.be/qJaFIGjyRms At 1:04:00 he sets the sign in method to: "await Google.logInAsync()" but I have noticed the google app auth library used in the video is now deprecated, I am redirected to use expo auth session instead but I notice there is a slight difference, where they used "await Google.logInAsync()" I must put "promptAsync" instead, when I do this I get the error promptAsync is undefined, I try with google.loginasync and get the same error that it is still undefined, what should I do? screenshot
code:
import React, { createContext, useContext } from 'react'
//import * as Google from 'expo-auth-session/providers/google';
import * as Google from 'expo-google-app-auth';
const AuthContext = createContext({});
const config = {
androidClientId:
'236293699216-9a0nknjdq7ie79h40iubg0tddokgogfv.apps.googleusercontent.com',
iosClientId:
'236293699216-6jdpm0rd6kn5d0qlbh1vgva5afgbqgib.apps.googleusercontent.com',
scopes: ["profile", "email"],
permissions: ["public_profile","email", "gender", "location"],
}
export const AuthProvider = ({ children}) => {
const signInWithGoogle = async() => {
await Google.logInAsync(config).then(async (logInResult) => {
if (logInResult.type === "success") {
// login
}
});
};
return (
<AuthContext.Provider
value={{
user: null,
signInWithGoogle
}}
>
{children}
</AuthContext.Provider>
)
}
export default function useAuth() {
return useContext(AuthContext);
}
I sought help on the forum that belongs to the maker of the video and other people had come across the same issue, one person recommended to go into package.json find the installed dependencies and change “expo-google-app-auth” from “^10.0.0” to “~9.0.0” and then npm I in the terminal, I have done this and I'm now getting the error “no such file or directory /Users/shangefagan/twinder-3/node_modules/expo-google-app-auth/node-modules/react-native/package.json” I have changed it back to “^10.0.0” but still getting the same error, screenshot
do I just npm uninstall expo-google-app-auth and try to use expo-auth-session as I was originally trying? if so What is the correct way to use promptAsync from the expo-auth-session library
I check the docs for both libraries, expo google app auth: https://docs.expo.dev/versions/v43.0.0/sdk/google/ and expo auth session: https://docs.expo.dev/versions/latest/sdk/auth-session/ but I am unsure exactly how to use the new login method "promptAsync"
To use promptAsync you have to use the package expo-auth-session. Like you said expo-google-app-auth is deprecated.

Deep linking launches the app, but handles the external url wrongly

I am developing an React Native project, using React-Navigation v5.
I now try to implement deep linking feature. I followed the official instruction set up deep linking successfully (I mean the app is launched by custom url scheme). Next I need to handle the external link, my issue comes at this point.
To handle external link in my react-native project, I also followed the instruction configure links.
I defined a linking.js in my project:
const config = {
screens: {
// I explained why I nest FooScreen like this if you continue reading
FeatureFlow: {
SubfeatureFlow: {
FooScreen: {
path: 'foo/:myId',
},
},
},
},
};
const linking = {
prefixes: ['myapp://mobile'],
config,
};
export default linking;
Then, in my NavigationContainer, I use the linking as below:
return (
<NavigationContainer
linking={linking}
...
>
<MainFlow />
</NavigationContainer>
As you can see above, three things worth a notice:
in linking.js, inside config I specified that the path e.g. foo/123 should open screen FooScreen.
The FooScreen is a nested screen.
NavigationContainer contains a component called MainFlow.
To illustrated how FooScreen is nested in navigation hierarchy, let's start with MainFlow, which looks like this:
const MainFlow = ({navigation}) => {
const Drawer = createDrawerNavigator();
return (
<Drawer.Navigator
...>
<Drawer.Screen name="FeatureFlow" component={MyFeatureFlow} />
...
</Drawer.Navigator>
);
};
As you can see, MainFlow is a DrawerNavigator which hosts a screen named FeatureFlow refers to component MyFeatureFlow.
And MyFeatureFlow looks like this:
const MyFeatureFlow = ({navigation}) => {
const FeatureStack = createStackNavigator();
return (
<FeatureStack.Navigator
...>
<FeatureStack.Screen
name="SubfeatureFlow"
component={MySubfeatureFlow}/>
</FeatureStack.Navigator>
)
As you can see above, FeatureFlow is a stack navigator which hosts a screen named SubfeatureFlow, refers to component MySubfeatureFlow.
And MySubfeatureFlow is like:
const MySubfeatureFlow = ({navigation}) => {
const SubfeatureStack = createStackNavigator();
return (
<SubfeatureStack.Navigator
...>
<SubfeatureStack.Screen
name="FooScreen"
component={MyFooScreen}
</SubfeatureStack.Navigator>
)
As you can see above, MySubfeatureFlow is another stack navigator which hosts a screen named FooScreen refers to component MyFooScreen.
Now you understand why in linking.js configuration, the FooScreen is nested like that.
Then, I run my app on iOS simulator. The app launched. I killed the app on simulator.
Then, I run command npx uri-scheme open myapp://mobile/foo/123 --ios. (I also tried open mobile browser, type in url myapp://mobile/foo/123, same error as below)
I see on simulator, the app is launched by the command, which means my deep linking is set up. However, handling external link to open FooScreen fails, I end up with the following error:
The error tells me the navigation payload tries to treat 123 as screen, and the app treat foo as name of the screen in my project. I completely don't understand why it is. Is there something wrong with my linking configuration?

How to setup sendSignInLinkToEmail() from Firebase in react-native?

Working on a react-native project using #react-native-firebase/app v6 we recently integrated signing in with a "magic link" using auth.sendSignInLinkToEmail
We couldn't find a good example on how to setup everything in react-native and had different problems like
- auth/invalid-dynamic-link-domain - The provided dynamic link domain is not configured or authorized for the current project.
- auth/unauthorized-continue-uri - Domain not whitelisted by project
Searching for information and implementing the "magic link login" I've prepared a guide on how to have this setup in react-native
Firebase project configuration
Open the Firebase console
Prepare firebase instance (Email Link sign-in)
open the Auth section.
On the Sign in method tab, enable the Email/Password provider. Note that email/password sign-in must be enabled to use email link sign-in.
In the same section, enable Email link (passwordless sign-in) sign-in method.
On the Authorized domains tab (just bellow)
Add any domains that will be used
For example the domain for the url from ActionCodeSettings needs to be included here
Configuring Firebase Dynamic Links
For IOS - you need to have an ios app configured - Add an app or specify the following throughout the firebase console
Bundle ID
App Store ID
Apple Developer Team ID
For Android - you just need to have an Android app configured with a package name
Enable Firebase Dynamic Links - open the Dynamic Links section
“Firebase Auth uses Firebase Dynamic Links when sending a link that is meant to be opened in a mobile application.
In order to use this feature, Dynamic Links need to be configured in the Firebase Console.”
(ios only) You can verify that your Firebase project is properly configured to use Dynamic Links in your iOS app by opening
the following URL: https://your_dynamic_links_domain/apple-app-site-association
It should show something like:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "AP_ID123.com.example.app",
"paths": [
"NOT /_/", "/"
]
}
]
}
}
IOS Xcode project configuration for universal links
Open the Xcode project and go to the Info tab create a new URL type to be used for Dynamic Links.
Enter a unique value in Identifier field and set the URL scheme field to be your bundle identifier, which is the default URL scheme used by Dynamic Links.
In the Capabilities tab, enable Associated Domains and add the following to the Associated Domains list: applinks:your_dynamic_links_domain
(!) This should be only the domain - no https:// prefix
Android
Android doesn’t need additional configuration for default or custom domains.
Packages
A working react-native project setup with react-native-firebase is required, this is thoroughly covered in the library own documentation, here are the specific packages we used
Note: using the dynamicLinks package can be replaced with react-native's own Linking module and the code would be almost identical
Exact packages used:
"#react-native-firebase/app": "^6.7.1",
"#react-native-firebase/auth": "^6.7.1",
"#react-native-firebase/dynamic-links": "^6.7.1",
Sending the link to the user email
The module provides a sendSignInLinkToEmail method which accepts an email and action code configuration.
Firebase sends an email with a magic link to the provided email. Following the link has different behavior depending on the action code configuration.
The example below demonstrates how you could setup such a flow within your own application:
EmailLinkSignIn.jsx
import React, { useState } from 'react';
import { Alert, AsyncStorage, Button, TextInput, View } from 'react-native';
import auth from '#react-native-firebase/auth';
const EmailLinkSignIn = () => {
const [email, setEmail] = useState('');
return (
<View>
<TextInput value={email} onChangeText={text => setEmail(text)} />
<Button title="Send login link" onPress={() => sendSignInLink(email)} />
</View>
);
};
const BUNDLE_ID = 'com.example.ios';
const sendSignInLink = async (email) => {
const actionCodeSettings = {
handleCodeInApp: true,
// URL must be whitelisted in the Firebase Console.
url: 'https://www.example.com/magic-link',
iOS: {
bundleId: BUNDLE_ID,
},
android: {
packageName: BUNDLE_ID,
installApp: true,
minimumVersion: '12',
},
};
// Save the email for latter usage
await AsyncStorage.setItem('emailForSignIn', email);
await auth().sendSignInLinkToEmail(email, actionCodeSettings);
Alert.alert(`Login link sent to ${email}`);
/* You can also show a prompt to open the user's mailbox using 'react-native-email-link'
* await openInbox({ title: `Login link sent to ${email}`, message: 'Open my mailbox' }); */
};
export default EmailLinkSignIn;
We're setting handleCodeInApp to true since we want the link from the email to open our app and be handled there. How to configure and handle this is described in the next section.
The url parameter in this case is a fallback in case the link is opened from a desktop or another device that does not
have the app installed - they will be redirected to the provided url and it is a required parameter. It's also required to
have that url's domain whitelisted from Firebase console - Authentication -> Sign in method
You can find more details on the supported options here: ActionCodeSettings
Handling the link inside the app
Native projects needs to be configured so that the app can be launched by an universal link as described
above
You can use the built in Linking API from react-native or the dynamicLinks #react-native-firebase/dynamic-links to intercept and handle the link inside your app
EmailLinkHandler.jsx
import React, { useState, useEffect } from 'react';
import { ActivityIndicator, AsyncStorage, StyleSheet, Text, View } from 'react-native';
import auth from '#react-native-firebase/auth';
import dynamicLinks from '#react-native-firebase/dynamic-links';
const EmailLinkHandler = () => {
const { loading, error } = useEmailLinkEffect();
// Show an overlay with a loading indicator while the email link is processed
if (loading || error) {
return (
<View style={styles.container}>
{Boolean(error) && <Text>{error.message}</Text>}
{loading && <ActivityIndicator />}
</View>
);
}
// Hide otherwise. Or show some content if you are using this as a separate screen
return null;
};
const useEmailLinkEffect = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const handleDynamicLink = async (link) => {
// Check and handle if the link is a email login link
if (auth().isSignInWithEmailLink(link.url)) {
setLoading(true);
try {
// use the email we saved earlier
const email = await AsyncStorage.getItem('emailForSignIn');
await auth().signInWithEmailLink(email, link.url);
/* You can now navigate to your initial authenticated screen
You can also parse the `link.url` and use the `continueurl` param to go to another screen
The `continueurl` would be the `url` passed to the action code settings */
}
catch (e) {
setError(e);
}
finally {
setLoading(false);
}
}
};
const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
/* When the app is not running and is launched by a magic link the `onLink`
method won't fire, we can handle the app being launched by a magic link like this */
dynamicLinks().getInitialLink()
.then(link => link && handleDynamicLink(link));
// When the component is unmounted, remove the listener
return () => unsubscribe();
}, []);
return { error, loading };
};
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(250,250,250,0.33)',
justifyContent: 'center',
alignItems: 'center',
},
});
const App = () => (
<View>
<EmailLinkHandler />
<AppScreens />
</View>
);
You can use the component in the root of your app as in this example
Or you can use it as a separate screen/route - in that case the user should be redirected to it after
the sendSignInLinkToEmail action
Upon successful sign-in, any onAuthStateChanged listeners will trigger with the new authentication state of the user. The result from the signInWithEmailLink can also be used to retrieve information about the user that signed in
Testing the email login link in the simulator
Have the app installed on the running simulator
Go through the flow that will send the magic link to the email
Go to your inbox and copy the link address
Open a terminal and paste the following code
xcrun simctl openurl booted {paste_the_link_here}
This will start the app if it’s not running
It will trigger the onLink hook (if you have a listener for it like above)
References
Deep Linking In React Native Using Firebase Dynamic Links
React Native Firebase - Dynamic Links
React Native Firebase - auth - signInWithEmailLink
firebase.google.com - Passing State In Email Actions
firebase.google.com - Authenticate with Firebase Using Email Link in JavaScript

how to use react-native-web and react-navigation together and access from web url

UPDATE:
react-navigation web support is done. follow this:
https://reactnavigation.org/docs/en/web-support.html
ORIGIN:
I try to share my code between react-native and web.
when I try react-native-web, it works well.
but there is only one question, how to access the specific screen from URL?
I read the react-navigation docs, there nothing about that.
and react-router-native can catch the web URL,
but it has navigator likes StackNavigator/DrawerNavigator.
and idea about that?
I'm not sure what the case was at the time you posted this question, but you definitely can use react-navigation with web now adays.
Now with Linking we can Handle deep links in React Native apps on Android and iOS, plus
Enable URL integration in browser when using on web.
The NavigationContainer component takes in a linking prop which allows you to map out your routes.
const linking = {
prefixes: ['https://mychat.com', 'mychat://'],
config: {
screens: {
Chat: 'feed/:sort',
Profile: 'user',
},
},
};
function App() {
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
{/* content */}
</NavigationContainer>
);
}
Once we establish what all of the routes or "links" are in our app we can start using Link components to navigate just like in a normal react web application if you used react-router-dom.
import { Link } from '#react-navigation/native';
// ...
function Home() {
return <Link to="/profile/jane">Go to Jane's profile</Link>;
}
These link components should work on both mobile, and web versions.
https://reactnavigation.org/docs/configuring-links/
https://reactnavigation.org/docs/deep-linking/
https://reactnavigation.org/docs/link
I don't think it's possible as ReactNavigation is using an internal state object. Remember, it's mobile framework, it has no concept of URL routing.
I also wanted to point out that even though RN claims web support you will need to be careful with component selection as not all the behaviours are identical (from memory, FlatList does not support touch scroll)