我正在构建一个带有react native和react导航库的移动应用程序,用于管理我的应用程序中的导航。现在,我的应用程序看起来像这样:
App [SwitchNavigator]
Splash [Screen]
Auth [Screen]
MainApp [StackNavigator]
Home [Screen] (/home)
Profile [Screen] (/profile)
Notifications [Screen] (/notifications)
我已经将Deep Linking与上面针对屏幕Home
、Profile
和Notifications
的模式集成在一起,并且它按预期工作。我面临的问题是如何在使用深度链接时管理用户的身份验证。现在,每当我打开深度链接(例如myapp://profile
)时,无论我是否通过身份验证,应用程序都会将我带到屏幕上。我希望它之前在AsyncStorage
中检查是否有userToken
,如果没有或不再有效,那么只需在Auth
屏幕上重定向。
我以与这里描述的几乎完全相同的方式设置身份验证流。因此,当我的应用程序启动时,Splash
屏幕会检查用户的手机是否有有效的令牌,并在Auth
屏幕或Home
屏幕上发送给他。
目前,我想到的唯一解决方案是将每个深度链接指向Splash
,验证我的用户,然后解析链接以导航到好的屏幕。例如,当用户打开myapp://profile
时,我在Splash
上打开应用程序,验证令牌,然后解析url(/profile
),最后重定向到Auth
或Profile
。
这是一个好方法吗,还是反应导航提供了一个更好的方法?他们网站上的深度链接页面有点轻。
谢谢你的帮助!
我的设置与您的类似。我遵循了身份验证流·React Navigation和SplashScreen-Expo Documentation来设置我的身份验证流,所以我有点失望,因为要让深层链接也能通过它,这是一个挑战。我能够通过定制我的主交换机导航器来实现这一点,这种方法与您所说的解决方案类似。我只是想分享我的解决方案,这样就有了一个具体的例子来说明如何开始工作。我的主开关导航器设置如下(我也在使用TypeScript,所以如果不熟悉类型定义,请忽略它们):
const MainNavigation = createSwitchNavigator(
{
SplashLoading,
Onboarding: OnboardingStackNavigator,
App: AppNavigator,
},
{
initialRouteName: 'SplashLoading',
}
);
const previousGetActionForPathAndParams =
MainNavigation.router.getActionForPathAndParams;
Object.assign(MainNavigation.router, {
getActionForPathAndParams(path: string, params: any) {
const isAuthLink = path.startsWith('auth-link');
if (isAuthLink) {
return NavigationActions.navigate({
routeName: 'SplashLoading',
params: { ...params, path },
});
}
return previousGetActionForPathAndParams(path, params);
},
});
export const AppNavigation = createAppContainer(MainNavigation);
您想要通过身份验证流路由的任何深度链接都需要从auth-link
开始,或者您选择的任何准备方法。以下是SplashLoading
的样子:
export const SplashLoading = (props: NavigationScreenProps) => {
const [isSplashReady, setIsSplashReady] = useState(false);
const _cacheFonts: CacheFontsFn = fonts =>
fonts.map(font => Font.loadAsync(font as any));
const _cacheSplashAssets = () => {
const splashIcon = require(splashIconPath);
return Asset.fromModule(splashIcon).downloadAsync();
};
const _cacheAppAssets = async () => {
SplashScreen.hide();
const fontAssetPromises = _cacheFonts(fontMap);
return Promise.all([...fontAssetPromises]);
};
const _initializeApp = async () => {
// Cache assets
await _cacheAppAssets();
// Check if user is logged in
const sessionId = await SecureStore.getItemAsync(CCSID_KEY);
// Get deep linking params
const params = props.navigation.state.params;
let action: any;
if (params && params.routeName) {
const { routeName, ...routeParams } = params;
action = NavigationActions.navigate({ routeName, params: routeParams });
}
// If not logged in, navigate to Auth flow
if (!sessionId) {
return props.navigation.dispatch(
NavigationActions.navigate({
routeName: 'Onboarding',
action,
})
);
}
// Otherwise, navigate to App flow
return props.navigation.navigate(
NavigationActions.navigate({
routeName: 'App',
action,
})
);
};
if (!isSplashReady) {
return (
<AppLoading
startAsync={_cacheSplashAssets}
onFinish={() => setIsSplashReady(true)}
onError={console.warn}
autoHideSplash={false}
/>
);
}
return (
<View style={{ flex: 1 }}>
<Image source={require(splashIconPath)} onLoad={_initializeApp} />
</View>
);
};
我使用routeName
查询参数创建了深度链接,这是执行身份验证检查后要导航到的屏幕的名称(很明显,您可以添加所需的任何其他查询参数)。由于我的SplashLoading
屏幕处理加载所有字体/资产以及身份验证检查,我需要每个深度链接来通过它。我面临的问题是,我会手动退出应用程序的多任务处理,点击深度链接url,并导致应用程序崩溃,因为深度链接绕过了SplashLoading
,因此无法加载字体。
上面的方法声明了一个action
变量,如果不设置该变量,它将不起任何作用。如果routeName
查询参数不是undefined
,我会设置action
变量。这使得一旦交换机路由器根据身份验证(Onboarding
或App
)决定采用哪条路径,该路由就会获得子操作,并在退出身份验证/启动加载流后导航到routeName
。
以下是我创建的一个示例链接,该链接在该系统中运行良好:exp://192.168.1.7:19000/--/auth-link?routeName=ForgotPasswordChange&cacheKey=a9b3ra50-5fc2-4er7-b4e7-0d6c0925c536
希望图书馆的作者将来能让这成为一个本机支持的功能,这样黑客就没有必要了。我也很想看看你想出了什么!
在我这边,我实现了这一点,而不必手动解析路由来提取路径¶ms。以下是步骤:
- 获取导航操作返回:getActionForPathAndParams
- 将导航操作作为param传递到"身份验证"视图
- 然后,当身份验证成功时,或者如果身份验证已经可以,我会调度导航操作,以按照预定路线进行
const previousGetActionForPathAndParams = AppContainer.router.getActionForPathAndParams
Object.assign(AppContainer.router, {
getActionForPathAndParams(path: string, params: NavigationParams) {
const navigationAction = previousGetActionForPathAndParams(path, params)
return NavigationActions.navigate({
routeName: 'Authentication',
params: { navigationAction }
})
}
})
然后在验证视图中:
const navigationAction = this.navigation.getParam('navigationAction')
if (navigationAction)
this.props.navigation.dispatch(navigationAction)
我最终使用了一个自定义URI来拦截deeplink的启动,然后将这些参数传递到预期的路由。我的加载屏幕处理身份验证检查。
const previousGetActionForPathAndParams = AppContainer.router.getActionForPathAndParams
Object.assign(AppContainer.router, {
getActionForPathAndParams (path, params) {
if (path === 'auth' && params.routeName && params.userId ) {
// returns a profile navigate action for myApp://auth?routeName=chat&userId=1234
return NavigationActions.navigate({
routeName: 'Loading',
params: { ...params, path },
})
}
return previousGetActionForPathAndParams(path, params)
},
})
https://reactnavigation.org/docs/en/routers.html#handling-自定义uris
然后,在我的Loading Route中,我像正常情况一样解析参数,但随后路由到预定位置,再次传递它们。
const userId = this.props.navigation.getParam('userId')
https://reactnavigation.org/docs/en/params.html
我发现了一种更简单的方法,我在一个单独的文件中维护链接,并将其导入主App.js
linking.js
const config = {
screens: {
Home:'home',
Profile:'profile,
},
};
const linking = {
prefixes: ['demo://app'],
config,
};
export default linking;
App.js
&在登录期间,我将令牌保存在异步存储中,当用户注销时,令牌将被删除。基于令牌的可用性,我将链接附加到导航并使用state&当它分离时,它会回落到SplashScreen。
确保设置initialRouteName="SplashScreen"
import React, {useState, useEffect} from 'react';
import {Linking} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {createStackNavigator} from '@react-navigation/stack';
import {NavigationContainer} from '@react-navigation/native';
import linking from './utils/linking';
import {Home, Profile, SplashScreen} from './components';
const Stack = createStackNavigator();
// This will be used to retrieve the AsyncStorage String value
const getData = async (key) => {
try {
const value = await AsyncStorage.getItem(key);
return value != null ? value : '';
} catch (error) {
console.error(`Error Caught while getting async storage data: ${error}`);
}
};
function _handleOpenUrl(event) {
console.log('handleOpenUrl', event.url);
}
const App = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => {
// Checks if the user is logged in or not, if not logged in then
// the app prevents the access to deep link & falls back to splash screen.
getData('studentToken').then((token) => {
if (token === '' || token === undefined) setIsLoggedIn(false);
else setIsLoggedIn(true);
});
Linking.addEventListener('url', _handleOpenUrl);
return () => {
Linking.removeEventListener('url', _handleOpenUrl);
};
}, []);
return (
//linking is enabled only if the user is logged in
<NavigationContainer linking={isLoggedIn && linking}>
<Stack.Navigator
initialRouteName="SplashScreen"
screenOptions={{...TransitionPresets.SlideFromRightIOS}}>
<Stack.Screen
name="SplashScreen"
component={SplashScreen}
options={{headerShown: false}}
/>
<Stack.Screen
name="Home"
component={Home}
options={{headerShown: false, gestureEnabled: false}}
/>
<Stack.Screen
name="Profile"
component={Profile}
options={{headerShown: false, gestureEnabled: false}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
当登录用户从通知中打开深度链接时,它将带他进入相应的深度链接屏幕,如果他没有登录,则它将从启动屏幕打开。
我创建了一个使用身份验证流进行自动深度链接的包。
您可以尝试一下。身份验证链接https://github.com/SohelIslamImran/auth-linking
身份验证链接
使用身份验证流的自动深度链接
深度链接很容易与身份验证一起使用。但有些人认为这很难。因此,此软件包将帮助您实现使用身份验证流处理深度链接的最简单方法。
安装
npm install auth-linking
yarn add auth-linking
用法
AuthLinkingProvider
你需要用AuthLinkingProvider
包装你的应用程序。
import AuthLinkingProvider from "auth-linking";
...
const App = () => {
return (
<AuthLinkingProvider onAuthChange={onAuthChange}>
{/* Your app components */}
</AuthLinkingProvider>
);
};
export default App;
onAuthChange
道具
您需要为AuthLinkingProvider
提供一个onAuthChange
道具。这个函数应该返回一个promise,其中包含user或truthy值(如果已登录)和null或falsy(如果用户未登录)。
const onAuthChange = () => {
return new Promise((resolve, reject) => {
onAuthStateChanged(auth, resolve, reject);
});
};
...
<AuthLinkingProvider onAuthChange={onAuthChange}>
使用自动重定向到深度链接
在完成所有身份验证流后将呈现的屏幕内调用此钩子。因此,这个钩子将自动重定向到打开应用程序的深层链接。
import { useAutoRedirectToDeepLink } from "auth-linking";
...
const Home = () => {
useAutoRedirectToDeepLink()
return (
<View>{...}</View>
);
};
全部完成。