苹果交接
了解如何使用 Expo Router 和 Apple Handoff 在 Apple 设备之间无缝地继续应用导航。
Apple Handoff 是一项功能,允许用户在另一台设备上继续浏览你的应用或网站。Expo Router 会自动处理此功能的所有运行时路由。然而,首次配置必须手动设置。
🌐 Apple Handoff is a feature that enables users to continue browsing your app or website on another device. Expo Router automates all of the runtime routing for this feature. However, the one-time configuration must be set up manually.
在 Expo Router 中,底层 iOS API(NSUserActivity)需要一个 webpageUrl,操作系统建议将其作为切换到你的应用的当前 URL。expo-router/head 组件有一个可选的原生模块,可以自动将 webpageUrl 设置为 Expo Router 中当前聚焦的路由。
🌐 In Expo Router, the underlying iOS API (NSUserActivity) requires a webpageUrl which the OS recommends as the current URL for switching to your app. The expo-router/head component has an optional native module that can automatically set the webpageUrl to the currently focused route in Expo Router.
设置
🌐 Setup
以下限制和注意事项很重要:
🌐 The following restrictions and considerations are important:
- Handoff 仅限 Apple。
- Handoff 不能在 Expo Go 应用中使用,因为它需要构建时配置。
- Handoff 需要配置通用链接,至少在 iOS 上,并且必须包含
activitycontinuation对象。 - 接力功能要求在你希望支持的每个页面上使用
expo-router/head组件,或者如果希望所有页面连续,则在根布局中使用。
为了确保 public/.well-known/apple-app-site-association 文件配置正确,它必须包含一个 activitycontinuation 键,键对应的 apps 数组中应包含你的应用的 bundle ID 和 Team ID,格式为 <APPLE_TEAM_ID>.<IOS_BUNDLE_ID>。例如,QQ57RJ5UTD.app.expo.acme,其中 QQ57RJ5UTD 是 Team ID,app.expo.acme 是 bundle 标识符。
🌐 To ensure that the public/.well-known/apple-app-site-association file is configured correctly, it must include the activitycontinuation key with an apps array that contains your app's bundle ID and Team ID formatted as <APPLE_TEAM_ID>.<IOS_BUNDLE_ID>. For example, QQ57RJ5UTD.app.expo.acme where QQ57RJ5UTD is the Team ID and app.expo.acme is the bundle identifier.
{ "applinks": { "details": [ { "appIDs": ["<APPLE_TEAM_ID>.<IOS_BUNDLE_ID>"], "components": [ { "/": "*", "comment": "Matches all routes" } ] } ] }, "activitycontinuation": { "apps": ["<APPLE_TEAM_ID>.<IOS_BUNDLE_ID>"] }, "webcredentials": { "apps": ["<APPLE_TEAM_ID>.<IOS_BUNDLE_ID>"] } }
webcredentials对象是可选的,但建议使用。
你可以使用以下命令根据你的应用配置生成 apple-app-site-association 文件:
🌐 You can use the following command to generate the apple-app-site-association file based on your app config:
- npx setup-safari请参阅 Test the deep link 指南,以在开发中测试交接。
🌐 See Test the deep link guide to test handoff in development.
Expo 头设置
🌐 Expo Head setup
确保使用 expo-router 配置插件在你的 app.config.tsx 文件中设置 Handoff 来源。这是用户切换到你的应用时将用于 webpageUrl 的 URL。
🌐 Ensure you set the Handoff origin in your app.config.tsx file using the expo-router config plugin. This is the URL that will be used for the webpageUrl when the user switches to your app.
// Be sure to change this to be unique to your project. process.env.EXPO_TUNNEL_SUBDOMAIN = 'bacon-router-sandbox'; const ngrokUrl = `${process.env.EXPO_TUNNEL_SUBDOMAIN}.ngrok.io`; /** @type {import('expo/config').ExpoConfig} */ module.exports = { // ... ios: { associatedDomains: [ `applinks:${ngrokUrl}`, `activitycontinuation:${ngrokUrl}`, `webcredentials:${ngrokUrl}`, // Add additional production-URLs here. // `applinks:example.com`, // `activitycontinuation:example.com`, // `webcredentials:example.com`, ], }, plugins: [ [ 'expo-router', { // Note: The URL must start with "https://" in "headOrigin" headOrigin: process.env.NODE_ENV === 'development' ? `https://${ngrokUrl}` : 'https://my-website-example.com', }, ], ], };
在测试与原生应用的交接时,不要使用仅用于开发的
?mode=developer后缀。
配置应用配置后,使用以下命令重新生成原生项目:
🌐 After configuring the app config, regenerate your native project with the following command:
- npx expo prebuild -p ios在开发中,你必须在将应用安装到设备上之前启动网站。这是因为当你安装应用时,操作系统会触发苹果的服务器去访问你网站上的 .well-known/apple-app-site-association 文件。如果网站没有运行,操作系统将无法找到该文件,handoff 功能将无法工作。如果发生这种情况,请使用 npx expo run:ios -d 重新构建原生应用。
🌐 In development, you must start the website before installing the app on your device. This is because when you install the app, the OS will trigger Apple's servers to ping your website for the .well-known/apple-app-site-association file. If the website is not running, the OS will not be able to find the file and handoff will not work. If this happens, rebuild the native app with npx expo run:ios -d.
用法
🌐 Usage
在任何你想支持切换的路由中,使用来自 expo-router/head 的 Head 组件:
🌐 In any route that you want to support handoff, use the Head component from expo-router/head:
import Head from 'expo-router/head'; import { Text } from 'react-native'; export default function App() { return ( <> <Head> <meta property="expo:handoff" content="true" /> </Head> <Text>Hello World</Text> </> ); }
元标签
🌐 Meta tags
expo-router/head 组件支持以下元标签:
🌐 The expo-router/head component supports the following meta tags:
| Meta tags | Description |
|---|---|
expo:handoff | Set to true to enable handoff for the current route. Defaults to false. (iOS only) |
og:title and <title> | Set the title for the NSUserActivity this is unused with handoff. |
og:description | Set the description for the NSUserActivity this is unused with handoff. |
og:url | Set the URL that should be opened when the user switches to your app. Defaults to the current URL in-app with headOrigin prop in the expo-router config plugin, as the baseURL. Passing a relative path will append the headOrigin to the path. |
你可能想在不同平台之间切换值,为此你可以使用 Platform.select:
🌐 You may want to switch the values between platforms, for that you can use Platform.select:
import Head from 'expo-router/head'; export default function App() { return ( <Head> <meta property="og:url" content={Platform.select({ web: 'https://expo.dev', default: null })} /> </Head> ); }
调试
🌐 Debugging
确保你的 Apple 设备已启用 接力。你可以按照以下步骤进行测试,只需用 Safari 替换你的应用即可。
🌐 Ensure your Apple devices have Handoff enabled. You can test this by following the steps below but substituting your app with Safari.
- 打开设备上的原生应用。
- 导航到应用中支持接力的路由,该路由正在从 Expo Router 渲染
<Head />元素。 - 要切换到 Mac,请单击 Dock 中应用的“Handoff”图标。
- 要切换到 iPhone 或 iPad,请打开应用切换器,然后点击屏幕底部的应用横幅。
如果你在 iPhone 的应用切换器中只看到 Safari 图标,则说明切换不起作用。
🌐 If you only see the Safari icon in your iPhone's App Switcher, then handoff is not working.
故障排除
🌐 Troubleshooting
你可以使用验证工具(例如 AASA Validator)来测试 Apple App 网站关联文件(public/.well-known/apple-app-site-association)。
🌐 You can test the Apple App Site Association file (public/.well-known/apple-app-site-association) by using a validator such as, AASA Validator.
如果你遇到问题,最好的办法是启用应用中最激进的切换设置。这可以确保任何可能的路径都可以链接。你可以通过确保 public/.well-known/apple-app-site-association 文件匹配所有路径来实现这一点:
🌐 If you're having issues, the best thing you can do is enable the most aggressive handoff settings in your app. This ensures that any possible route is linkable. You can do this by making sure that public/.well-known/apple-app-site-association file matches all routes:
{ "applinks": { "details": [ { "appIDs": ["<APPLE_TEAM_ID>.<IOS_BUNDLE_ID>"], "components": [ { "/": "*", "comment": "Matches all routes" } ] } ] } }
在应用中,确保不要有条件地渲染 <Head /> 元素(例如,在 if/else 块中),它必须在每个希望支持交接的页面上渲染。我们建议将其添加到 Root Layout 组件中,以确保在调试时每个路由都可以链接。
🌐 In the application, ensure you are not rendering the <Head /> element conditionally (for example, in an if/else block), it must be rendered on every page that you want to support handoff. We recommend adding it to the Root Layout component to ensure every route is linkable while debugging.
在将应用安装到设备之前,请确保你可以访问 Ngrok URL(例如,通过浏览器)。如果无法访问该 URL,操作系统将无法找到文件,交接也将无法正常工作。
🌐 Ensure you can access the Ngrok URL (for example, via the browser), before installing the app on your device. If you can't access the URL, the OS will not be able to find the file and handoff will not work.
npx expo run:ios 和 Xcode 都会在设置关联域时对你的应用进行代码签名,这是确保 Handoff 和通用链接正常工作的必要条件。
在 Expo Go 应用中不支持 Mac 与 iPhone/iPad 之间的交接功能。你必须在设备上构建并安装你的应用。
🌐 Handoff between your Mac and iPhone/iPad is not supported in the Expo Go app. You must build and install your app on your device.
如果你在 iPhone 的应用切换器中看到 Safari 图标,那就意味着接力功能没有正常工作。
- 确保在测试交接给原生端时不要使用
?mode=developer后缀。 - 还要确保你没有使用本地开发服务器的 URL。例如,
http://localhost:8081不能用作有效的应用网站关联链接,请在浏览器中打开正在运行的 Ngrok URL 进行测试。 - 确保你的 public/.well-known/apple-app-site-association 文件包含
activitycontinuation字段。 - 我们观察到在 iOS 16.3.1 和 macOS 13.0(Ventura)中,以
app.和io.开头的应用包标识符有时不会触发原生应用在 iOS 任务切换器中显示。请将com.用作应用包标识符的开头部分。
你的 public/.well-known/apple-app-site-association 必须通过安全 URL(HTTPS)提供。如果你正在使用开发隧道,必须使用 EXPO_TUNNEL_SUBDOMAIN 环境变量来配置开发隧道的子域。开发阶段测试时需要隧道,因为使用通用链接需要 SSL,Expo CLI 通过运行 npx expo start --tunnel 提供了内置支持。
🌐 Your public/.well-known/apple-app-site-association must be served from a secure URL (HTTPS). If you are using a development tunnel, you must use the EXPO_TUNNEL_SUBDOMAIN environment variable to configure the subdomain for your development tunnel. The tunnel is required for testing in development because you need SSL to use universal links, Expo CLI provides built-in support for this by running npx expo start --tunnel.
检查你的 ios/project/project.entitlements 文件,在 com.apple.developer.associated-domains 键下。这里应该包含与你的 Web 服务器/网站相同的域名。URL 不能包含协议(https://),也不能有额外的路径名、查询参数或片段。
🌐 Check your ios/project/project.entitlements file, under the com.apple.developer.associated-domains key. This should contain the same domains as your web server/website. The URL cannot contain a protocol (https://) or additional pathname, query parameters, or fragments.
仍然卡住
🌐 Still stuck
这是一个重要但非常难以设置的功能。Expo Router 自动化了许多流程,Expo CLI 自动化了大部分配置和托管。然而,硬件设置仍然可能配置错误。
如果一切方法都失败了,你可以尝试按照 Apple 文档 中的步骤来调试问题。请注意:
🌐 If all else fails, you can try to debug the issue by following the steps in the Apple Docs. Note that:
- 将用户活动表示为
NSUserActivity的实例是由 Expo Head 原生模块执行的。 - “当用户在你的应用中执行操作时更新活动实例。”是通过在内部挂载/渲染带有
<meta property="expo:handoff" content="true" />元标签的<Head />组件来完成的。 - “在其他设备上的应用中接收来自 Handoff 的活动。” 由 Expo Head 原生模块中的 App Delegate Subscriber 执行。它用于在你跳转到原生应用时将你重定向到正确的路由。
已知的问题
🌐 Known issues
从网页切换到原生应用不支持客户端路由。这意味着在应用切换器中显示的 URL 将是你点击链接或重新加载页面时所在的页面的 URL。这是网页平台的一个限制,并不是 Expo Router 可以解决的问题。
🌐 Handoff from web to native does not support client-side routing. This means the URL presented in the App Switcher will be the URL of the page you were on when you clicked the link, or reloaded the page. It is a limitation of the web platform and not something that can be fixed by Expo Router.