所有文章
mobiledeep linksiOSAndroid

用 localhost 隧道测试移动端深度链接

iOS 上的 Universal Links 和 Android 上的 App Links 都要求在你域名的根目录有一个通过 HTTPS 提供的 JSON 文件。iOS 要 /.well-known/apple-app-site-association。Android 要 /.well-known/assetlinks.json。localhost 无法通过一个操作系统会与你应用关联的主机名提供其中任何一个。所以我们用隧道。

每个平台的实际要求

iOS Universal Links

你的应用安装时,操作系统会获取 https://your-domain.com/.well-known/apple-app-site-association(或 /apple-app-site-association)。它根据你应用的关联域名 entitlement 检查内容。获取必须通过真实 HTTPS 成功。iOS 不会为此信任自签证书,也不会信任 localhost。

Android App Links

Android 通过获取 https://your-domain.com/.well-known/assetlinks.json 来验证 App Links。该文件必须列出你应用的 SHA-256 指纹。验证发生在安装时和首次启动时。同样的约束:真实 HTTPS、真实域名。

为什么 localhost 无法直接做到

即使 mkcert 在你笔记本浏览器里给了你受信的 HTTPS,移动操作系统也不信任你的本地 CA。而且你 apple-app-site-association 里的域名必须与应用 entitlement 中的关联域名一致——那是一个真实的、可由 DNS 解析的域名,不是 localhost

有些团队用一个专门的 staging 域名绕过这点。这能行,但迭代循环很慢。一个 localhost 隧道给你一个真实子域名上的真实 HTTPS URL,移动设备能毫无怨言地到达。

如何接好

  1. 从你的本地开发服务器在 /.well-known/ 提供 apple-app-site-associationassetlinks.json。两个文件。两条路由。
  2. 运行 npx portpreview 3000(或你开发服务器用的端口)。
  3. 记下隧道主机名——类似 abc123.portpreview.dev
  4. 在你 iOS 应用的关联域名 entitlement 里加上 applinks:abc123.portpreview.dev。在 Android 的 intent filter 里加同一个 host。
  5. 在真实设备上构建并安装应用(模拟器的 Universal Link 行为很怪)。
  6. 从另一个应用——备忘录、邮件、一个二维码——打开该 URL,看它路由到你已安装的应用而非浏览器。

为深度链接做隧道的坑:除非你有保留子域名,否则隧道主机名会在会话间变化。每次轮换都意味着用新的关联域名条目重新构建应用。如果你频繁迭代深度链接行为,弄一个保留子域名。

content-type 对 apple-app-site-association 很重要

iOS 期望文件无扩展名,且 content-type 为 application/json(较新 iOS)或 application/pkcs7-mime(较旧的签名格式)。几乎所有现代应用都用纯 JSON 变体。确保你的开发服务器返回正确的 content-type,否则 iOS 会默默拒绝文件且没有有用的错误。

先从桌面浏览器测试:打开 https://your-tunnel.portpreview.dev/.well-known/apple-app-site-association,确认 JSON 能渲染且 dev tools 里的 content-type 头正确。如果不对,先修好再去模拟器里追 Universal Link 的 bug。

其他深度链接调试陷阱

应用 entitlement 不匹配

如果你的关联域名 entitlement 写 applinks:abc.portpreview.dev 但 apple-app-site-association 列的是 def.portpreview.dev,iOS 不会去获取。主机名必须一致。

从 Safari 内部点 Universal Link

在 Safari 内(标签页所在的同一应用)点 Universal Link 有时会在 Safari 里打开而非应用。这是设计如此——iOS 防止“用户每次点链接就打开应用”的把戏。改从备忘录或邮件测试。

Android App Links 与 digital asset links

Android 也支持自定义 URL schemeyourapp://path),不需要 HTTPS 或 assetlinks.json。它们更容易测试但安全性较低——任何应用都能注册同样的 scheme。要生产级的深度链接,App Links 才是答案。

我们用的移动测试流程

对于一个带深度链接、同时发布 iOS 和 Android 的项目:

  1. 启动提供 .well-known 文件的后端。
  2. npx portpreview 3000 给它做隧道。
  3. 一旦隧道 URL 在本次会话稳定(或用保留子域名),更新应用的关联域名条目并构建。
  4. 在物理设备上做 QA——全新安装和更新都要。
  5. 对于 OAuth 与深度链接组合(登录提供商重定向回应用),把它和 OAuth 回调测试搭配。

第一次设置很烦。之后,隧道 URL 只是 Xcode 和 Gradle scheme 里的又一个环境变量。

关于更广泛的移动测试模式,见用 localhost 隧道做移动测试加入 PortPreview 等候名单

常见问题

我能在 localhost 上测试 iOS Universal Links 吗?
不能直接测。Universal Links 要求 iOS 通过真实 HTTPS 从真实域名获取 apple-app-site-association。localhost 不符合。隧道给你一个隧道子域名上的 HTTPS URL,只要你把它列进关联域名 entitlement,iOS 就会把它与你的应用关联。
apple-app-site-association 该用什么 content-type?
现代 iOS 期望 application/json。旧版本也接受用于签名文件的 application/pkcs7-mime。如今大多数应用用纯 JSON 变体。如果 iOS 没拾取你的文件,先从桌面浏览器检查 content-type 头。
Android App Links 本地测试需要隧道吗?
如果你想要完整的 App Link 验证(而非自定义 URL scheme),需要。Android 通过真实 HTTPS 从你 intent filter 里的域名获取 assetlinks.json。隧道在一个设备可验证的域名上提供真实 HTTPS URL。自定义 scheme 不需要这个,但安全性较低。