深度链接
Universal Links 与 soar:// 自定义 scheme,从捕获到路由。
应用通过一个由三条捕获通道供给的单槽处理器,把外部 URL 路由到具体界面。本页讲两种入口类型、URL 如何变成
DeepLinkDestination,以及外壳在冷启动与热启动下如何消费它。它是 Integrated——自定义 scheme 开箱即用;
Universal Links 需要一个 entitlement + 一个托管文件。
关键文件
两条入口通道
Prop
Type
模板已注册 soar scheme,且 entitlement 已存在但指向占位域名:
<!-- SoarStarterSwift.entitlements -->
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
</array>要启用 Universal Links,把 example.com 换成你的 LINKING_DOMAIN,然后在
https://<domain>/.well-known/apple-app-site-association 托管一个 apple-app-site-association 文件,
声明你应用的 appID(<TeamID>.com.soarstarter.SoarStarterSwift)。在此之前,只有 soar:// scheme 可解析。
三个捕获来源 → 一个处理器
每个入口都汇入 DeepLinkHandler.set(_:):
.onOpenURL (RootView) ← 应用前台/启动时的 soar://
application(_:continue:) (AppDelegate) ← Universal Link 点击(NSUserActivity)
notification tap (NotificationsController) ← 推送载荷的 deeplink/url
│
▼
DeepLinkHandler.pending : DeepLinkDestination? (单槽,@Observable)
│
▼
MainShellView 在 .onAppear + .onChange(of: pending) 时消费RootView.onOpenURL 先让 Google 登录与 Supabase 的 auth 回调有机会认领该 URL(soar://auth-callback),
再把其余交给 DeepLinkHandler。
解析一个 URL
DeepLinkResolver.resolve(_:) 归一化两种 URL 形态并映射到 DeepLinkDestination 枚举,未知路径返回
nil(会被忽略):
- 对
soar://todos,host 承载路由;对https://domain/todos,path 承载——解析器把二者拼接, 因此两种形态都能用。 ?route=查询会覆盖路径,所以推送载荷可用查询参数携带目标(如soar:///notifications?route=me)。- 路径会被归一化(补前导斜杠、折叠
//、去尾部斜杠)。
可路由的目标
| 路径 | 目标 | 落到 |
|---|---|---|
/、/home | .home | Home 标签页 |
/component | .component | 组件标签页 |
/showcase | .showcase | Showcase 标签页(可移除的演示) |
/todos | .todos | Home 标签页 → Todos |
/me、/profile | .me | Me 标签页 |
/personal-info | .personalInfo | Me → 个人信息 |
/notifications | .notifications | Me → 通知 |
/permissions | .permissions | Me → 权限 |
/refer | .refer | Me → 推荐 |
/paywall | .paywall | Me → 付费墙(需要时提示登录) |
/redeem-code | .redeemCode | Me → 兑换码(提示登录) |
冷启动 vs 热启动
MainShellView 在两处排空处理器,使两种情况都生效:
- 热启动——
.onChange(of: env.deepLinks.pending)在外壳已在屏时,URL 一到即触发。 - 冷启动——启动期间捕获的 URL 在外壳首次出现时仍在
pending中,于是.onAppear消费它。
routePendingDeepLink() 调用 consume()(读取后清空槽),关闭抽屉,选中目标标签页,并设置该标签页的
导航路径。由于每个标签页有各自的 @State 路径,路由到 .todos 会设置 homePath = [.todos] 而不扰动
其他标签页的返回栈。付费墙 / 兑换目标在登出时会先弹出登录表单。
推荐分享流程
ReferralLinkProvider.inviteURL() 用 LINKING_DOMAIN(https://<domain>/)构建分享 URL,未设域名时
回退到 soar:///。推荐界面用 ShareLink 包装它并触发 referral invite shared 分析事件——很适合放一个
回到应用的 ?route=refer 深度链接。
测试
自定义 scheme 链接在模拟器里就能用——无需服务端、无需真机:
xcrun simctl openurl booted "soar://todos"
xcrun simctl openurl booted "soar:///notifications?route=me"内置的 DeepLinksShowcaseView(在可移除的 Showcase 中)把可路由目标列为可点按钮,便于快速手动巡检。
Universal Links 只能在 AASA 文件上线后于真机验证。
验证
-
xcrun simctl openurl booted "soar://todos"打开 Home 标签页的 Todos。 - 登出时
soar:///paywall先弹登录表单,再进付费墙。 - 未知路径(
soar://nope)被忽略(不崩溃、不导航)。
