关于 capacitor-deep-linking
为 Capacitor 开发者提供深度链接配置与排查指导,涵盖自定义 scheme、iOS Universal Links 和 Android App Links,并处理冷启动与热启动路由差异。
主要功能
- 01配置自定义 scheme 与已验证链接
- 02处理冷启动和热启动 URL
- 03适配 iOS SceneDelegate 生命周期
- 04校验链接并验证平台配置
- 05排查 OAuth 等回跳问题
适用场景
- 01将 OAuth 或支付回调路由回应用
- 02修复链接启动应用后落到首页的问题
- 03诊断 Android 域名验证失败
- 04适配 Capacitor 8.5+ 深度链接变更
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ cap-go/ capgo-skills --skill capacitor-deep-linking安装后在 Claude Code 中输入/capacitor-deep-linking确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.jsonreferences/android-app-links.mdios-scene-delegate.mdios-universal-links.mdrouting-patterns.md详细介绍
Capacitor 中的深层链接
将自定义协议链接、iOS 通用链接和 Android 应用链接路由到 Web 应用。
适用场景
满足以下情况时触发:
- 为 Capacitor 应用添加
myapp://协议、通用链接或应用链接 - 链接打开的是浏览器而非应用,或打开了应用但停留在首页
- 温启动时触发了
appUrlOpen,但冷启动时缺少 URL(或反之) - 更新到 Capacitor 8.5+ / 采用
SceneDelegate.swift后深层链接失效 - OAuth / 魔法链接 / 支付重定向必须返回应用
adb shell pm get-app-links显示none或1024,而非verified
请勿用于:
- 将整个应用从 AppDelegate 迁移到 SceneDelegate:
capacitor-uiscene-migration - 通知点击路由:
capacitor-push-notifications - 调试时捕获原生日志:
ios-android-logs - Sign in with Apple / Google 插件配置:
capacitor-plugins
链接类型
| 类型 | 平台 | 格式 | 需要托管文件 | 已验证所有权 |
|---|---|---|---|---|
| 自定义协议 | 两者 | myapp://product/123 | 否 | 否(任何应用都可以声明) |
| 通用链接 | iOS | https://example.com/product/123 | /.well-known/apple-app-site-association | 是 |
| 应用链接 | Android 6+ | https://example.com/product/123 | /.well-known/assetlinks.json | 是 |
对于任何涉及安全的用途(OAuth、魔法链接),优先使用已验证的 https 链接。其他应用可以劫持自定义协议。
工作流
- 先检查。 阅读
capacitor.config.*、ios/App/App/Info.plist、ios/App/App/*.entitlements、ios/App/App/AppDelegate.swift、ios/App/App/SceneDelegate.swift(如果存在)、android/app/src/main/AndroidManifest.xml,以及package.json中的@capacitor/app版本。 - 确定 iOS 生命周期路径。
- Info.plist 中存在
UIApplicationSceneManifest(Capacitor 8.5+ 模板或已迁移的应用)-> URL 事件会到达SceneDelegate。AppDelegate 中的任何自定义application(_:open:options:)或application(_:continue:restorationHandler:)代码都是无效代码。参见 references/ios-scene-delegate.md。 - 没有场景清单 -> 在 8.5 core 上仍可使用 AppDelegate 路径,但 Xcode 27 / Capacitor 9 要求采用场景生命周期。将迁移交由
capacitor-uiscene-migration处理。
- Info.plist 中存在
- 安装
@capacitor/app(npm install @capacitor/app && npx cap sync)。 - 在应用启动时注册一次 JS 处理器(在路由器渲染之前),同时使用
appUrlOpen和getLaunchUrl()。参见下文。 - 配置平台:自定义协议(Info.plist
CFBundleURLTypes、Android<intent-filter>),然后按平台配置已验证链接:- iOS 通用链接:references/ios-universal-links.md
- Android 应用链接:references/android-app-links.md
- 路由:在框架路由器中配置:references/routing-patterns.md(React、Vue、Angular、OAuth 回调、延迟链接)。
- 验证:在真实设备上使用下方的冷启动 / 温启动矩阵。
仅当某个主题适用时,才加载相应参考资料。
JS 处理器(每个应用都需要的部分)
import { App, type URLOpenListenerEvent } from '@capacitor/app';
let lastHandled: string | undefined;
function route(rawUrl: string) {
if (rawUrl === lastHandled) return; // cold start can deliver the same URL to both paths
lastHandled = rawUrl;
const url = new URL(rawUrl);
// myapp://product/123 -> host "product", pathname "/123"
// https://example.com/product/123 -> pathname "/product/123"
const path = url.protocol.startsWith('http')
? url.pathname
: `/${url.host}${url.pathname}`;
router.navigate(path + url.search); // your router
}
export async function initDeepLinks() {
await App.addListener('appUrlOpen', (event: URLOpenListenerEvent) => route(event.url));
const launch = await App.getLaunchUrl(); // resolves undefined or { url }
if (launch?.url) route(launch.url);
}
注意事项:
- 自定义协议 URL 解析后,第一个片段位于
host而非pathname。请按上述方式规范化两种形式。 - 在应用启动时注册监听器,而非在延迟加载的页面中注册。事件触发后再添加的监听器无法收到该事件;冷启动时可通过
getLaunchUrl()补足。 - 请将监听器中的状态更新封装在框架的 zone / 运行循环中(Angular 使用
NgZone.run),否则导航不会有任何效果。 - 去重:冷启动时可通过
getLaunchUrl()和appUrlOpen两种方式获取该 URL。 - 导航前验证路径并设置允许列表;深层链接是不可信输入。
自定义 Scheme 配置
iOS ios/App/App/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.example.app</string>
<key>CFBundleURLSchemes</key>
<array><string>myapp</string></array>
</dict>
</array>
Android AndroidManifest.xml,位于现有的 MainActivity 中(保留模板中的 android:launchMode="singleTask",以便热启动时复用 Activity):
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
验证
在实体设备上运行每一行(通用链接无法在模拟器地址栏中可靠工作;请从 Notes/Messages 中点按链接)。
| 场景 | iOS | Android |
|---|---|---|
| 自定义 scheme,热启动 | xcrun simctl openurl booted "myapp://product/123" | adb shell am start -W -a android.intent.action.VIEW -d "myapp://product/123" com.example.app |
| 自定义 scheme,冷启动 | 结束应用,然后运行相同的命令 | adb shell am force-stop com.example.app,然后运行相同的命令 |
| 已验证链接,热启动 | 在 Notes 中点按 https://example.com/product/123 | adb shell am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://example.com/product/123" |
| 已验证链接,冷启动 | 将应用从后台划掉,然后点按链接 | 强制停止应用,然后运行相同的命令 |
| 所有权检查 | curl -sI https://example.com/.well-known/apple-app-site-association(200,且无重定向) | adb shell pm get-app-links com.example.app 显示 verified |
通过标准:每种情况都会进入正确的路由,appUrlOpen 只记录一次;冷启动时,getLaunchUrl() 会返回该 URL。完成 Capacitor 8.5 场景迁移后,请重新运行完整测试矩阵:冷启动会采用与热启动不同的原生路径(scene(_:willConnectTo:options:),而热启动使用 scene(_:openURLContexts:) / scene(_:continue:))。
迁移后,使用 grep 查找已失效的处理程序:
grep -n "open url: URL\|continue userActivity" ios/App/App/AppDelegate.swift
grep -n "SceneDelegateProxy" ios/App/App/SceneDelegate.swift
错误处理
| 症状 / 消息 | 原因 | 修复方法 |
|---|---|---|
| 链接在 Safari 中打开,但应用已安装 | 尚未获取 AASA、appID 错误、缺少 entitlement,或用户长按链接并选择了“在 Safari 中打开” | 通过 Apple CDN 检查 AASA、TEAMID.bundleId 和 applinks: entitlement;长按链接并选择“在 App 中打开”以重置 |
| 8.5 更新后,自定义 AppDelegate URL 代码不再运行 | 存在 Scene manifest;iOS 会改为调用 SceneDelegate | 将逻辑移至 SceneDelegate(参考:ios-scene-delegate) |
| 热启动正常,冷启动却进入主页 | 监听器注册过晚,或自定义 SceneDelegate 代码忽略了 connectionOptions | 在引导启动时调用 getLaunchUrl();保留 SceneDelegateProxy.shared.scene(_:willConnectTo:options:) |
Safari cannot open the page because the address is invalid | 已安装的构建中未注册自定义 scheme | 添加 CFBundleURLTypes,然后重新构建(不能只运行 cap sync) |
Error: Activity not started, unable to resolve Intent | 没有 intent-filter 与 scheme/host 匹配 | 修正 <data> 条目;重新安装应用 |
pm get-app-links 显示 1024 或 none | 无法访问 assetlinks.json、软件包错误,或 SHA-256 错误(通常是 Play App Signing 密钥) | 修正文件,然后运行 adb shell pm verify-app-links --re-verify com.example.app |
| Android 对 https 链接显示应用选择器 | 域名未验证(缺少 autoVerify 或验证失败) | 添加 android:autoVerify="true",然后重新验证 |
| OAuth 重定向返回浏览器标签页,而非应用 | 重定向 URI 与已注册的 scheme / 已验证域名不匹配 | 注册确切的重定向 URI;优先使用已验证的 https 链接 |
资源
- Capacitor 深层链接指南:https://capacitorjs.com/docs/guides/deep-links
- App 插件 API:https://capacitorjs.com/docs/apis/app
- 更新至 8.5(UIScene):https://capacitorjs.com/docs/updating/8-5
- Apple 通用链接:https://developer.apple.com/documentation/xcode/supporting-associated-domains
- Android App Links:https://developer.android.com/training/app-links/verify-android-applinks
评论
支持 Markdown