Universal Link 是 Apple(iOS 9+)的一种机制,允许直接在应用中打开网页链接,绕过 Safari。如果应用未安装,链接会无缝在浏览器中打开。该术语由 Apple 于 2015 年在 WWDC 上作为 Handoff 和 Continuity 生态系统的一部分引入。根据 Apple Developer,Universal Link 在 Web 和原生应用之间提供统一的用户体验,无需选择对话框。
要点
Universal Link 是一种标准 HTTPS 链接,格式为 https://example.com/page,当从 iOS 设备点击时,会打开已安装的应用而不是 Safari。与 Custom URL Scheme 的主要区别:Universal Link 不需要注册自定义方案(myapp://)——它使用普通域名。这消除了 URL Scheme hijacking 的问题,即任何应用都可以注册相同的方案。
Apple 在 WWDC 2015 上随着 iOS 9 引入了 Universal Link。该机制成为 Handoff 和 Spotlight 生态系统的一部分:Universal Link 不仅在浏览器中工作,还在 Spotlight 搜索结果、Mail、Messages 和其他系统应用中工作。此外,Universal Link 在 watchOS 和 macOS 中也受支持——用户可以通过 Mac 上的链接在 iPhone 上打开应用。
关键优势:统一 URL。开发者不需要管理两个不同的链接(一个用于 Web,一个用于应用)。Universal Link 是同一个 https 链接。如果应用已安装——它会被打开。如果没有——同一链接会在 Safari 中作为普通网页打开。这提供了理想的 fallback,不会损失流量。
Universal Link 机制由三个阶段组成:关联验证、链接处理和浏览器 fallback。每个阶段对正确运行都至关重要。如果未配置关联,iOS 会将链接视为普通跳转到 Safari。我们详细看看每个阶段。
第一次点击链接时,iOS 从服务器 https://example.com/.well-known/apple-app-site-association 下载 apple-app-site-association 文件。该文件包含带有 Team ID 和 Bundle ID 的 JSON,以及应用应打开的路径列表。iOS 会缓存此文件并定期检查其更新(在应用更新、设备重启时)。
JSON 文件 apple-app-site-association 必须通过 HTTPS 可访问,且无重定向。服务器必须返回 Content-Type: application/json。重要:该文件没有 .json 扩展名——iOS 严格在 /.well-known/apple-app-site-association 路径下查找。Apple 还建议在 CDN 中添加 Universal Link 支持,并检查该文件未被 robots.txt 阻止。
// apple-app-site-association — 最小配置
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAMID.com.example.app",
"paths": ["/product/*", "/profile/*", "/search"]
}
]
}
}
appID 由 Team ID + Bundle ID(TEAMID.com.example.app)组成。paths — 应用应处理的 URL 模式数组。可以使用 *、? 和 NOT 符号:["NOT /admin/*", "/product/*"]。路径按枚举顺序检查:第一个匹配决定行为。如果路径不匹配——链接在 Safari 中打开。
成功验证关联后,iOS 将链接传递给应用。处理在 AppDelegate 中通过 application(_:continue:restorationHandler:) 方法针对 NSUserActivity 进行,或在 SceneDelegate 中通过 scene(_:continue:) 进行。开发者收到具有 NSUserActivityTypeBrowsingWeb 类型的 NSUserActivity 对象,提取 URL 并导航到相应的屏幕。
// 在 AppDelegate 中处理 Universal Link
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping UIUserActivityRestorationHandler
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL
else { return false }
// 根据 URL 导航到相应屏幕
DeepLinkRouter.navigate(to: url)
return true
}
上述示例中的 DeepLinkRouter 是一个自定义类,解析 URL 并调用相应的导航协调器。对于 SwiftUI,处理通过 onOpenURL 方法或 environment(\.openURL) 修饰符进行。重要的是不仅要处理前台启动(foreground),还要处理应用未启动的情况(cold start):Universal Link 在这种情况下通过启动选项打开应用。
如果应用未安装,iOS 会自动在 Safari 中打开 Universal Link。这是与 Custom URL Scheme 的主要区别:用户不会看到错误。Fallback 是同一域名的标准网页。开发者可以在该页面上放置 App Store 链接、产品信息或备选内容。
重要:fallback 不能在 iOS 级别自定义。iOS 只是在 Safari 中打开 URL。要为已安装和未安装应用的用户显示不同内容,请使用 Smart App Banner(Safari 的元标记,建议打开应用)或通过 JavaScript 检测应用安装。Apple 还提供 SKAdNetwork 用于通过 Universal Link 归因安装。
Universal Link 和传统的 Deep Link(Custom URL Scheme)解决相同的任务,但在架构和安全性上存在根本差异。Custom URL Scheme — 在 Info.plist 中注册的自定义协议(myapp://)。任何应用都可以注册相同的方案(myapp://),iOS 无法确定哪个是“真正的”。这被称为 URL Scheme hijacking。
Universal Link 通过域名验证解决了 hijacking 问题。只有域名所有者才能在其服务器上放置 apple-app-site-association,确认与特定 Bundle ID 的关联。两个应用不能注册同一个 Universal Link:如果发生冲突,iOS 优先处理最后安装的应用或打开 Safari。
另一个区别:Fallback。Custom URL Scheme 没有 fallback——如果应用未安装,浏览器显示错误。Universal Link 打开网站。统一 URL 意味着链接的 SEO 价值得以保留(链接被 Google 索引),任何设备的用户都能获得相关内容。Universal Link 是从 deep link 到 unified link 的演进步骤。
| 特性 | Custom URL Scheme | Universal Link |
|---|---|---|
| 格式 | myapp://path | https://domain/path |
| 验证 | 无 | apple-app-site-association |
| 安全性 | 易受 hijacking 攻击 | 仅域名所有者 |
| Fallback | 错误 | Safari 中的网站 |
| iOS 版本 | iOS 3+ | iOS 9+ |
配置 Universal Link 包括服务器端和客户端。服务器端——将 apple-app-site-association 文件放在 https://domain/.well-known/apple-app-site-association 地址。客户端——在 Xcode 的 Associated Domains 中注册域名(Capabilities → Associated Domains → applinks:example.com)。之后,应用会自动接收指定域名的所有 Universal Link。
配置步骤:
调试 Universal Link — iOS 开发者的常见头疼问题。链接不工作的主要原因:apple-app-site-association 文件无法通过 HTTPS 访问,appID 错误,Content-Type 不是 application/json,从 /.well-known 路径重定向,旧版本缓存(通过 Settings → Developer → Associated Domains Development 重置)。Apple 在 Apple Developer Console 中提供 "Validation Checker" 工具用于测试关联。
Branch 和其他 MMP 平台简化了 Universal Link 的配置:它们自动生成 apple-app-site-association 并在自己的域名上托管。开发者只需将 Branch 域名添加到 Associated Domains 并集成 SDK。这对于没有自己的服务器基础设施来托管 AASA 文件的初创公司尤其方便。
Universal Link 有几个限制。第一:apple-app-site-association 文件必须严格通过 HTTPS 访问(不支持 HTTP)。第二:链接必须指向与 Associated Domains 中指定的相同域名。跨域 Universal Link 不工作——每个域名需要在 Capabilities 中有单独的条目和单独的 AASA 文件。第三:Universal Link 在 WKWebView 中不工作——仅在 Safari 和系统组件中。
兼容性:iOS 9.0+(Universal Link),watchOS 6.0+(Handoff Universal Link),macOS 10.15+(Catalyst 和 Mac 应用)。在较旧的 iOS 版本中,链接在 Safari 中打开。这意味着在 iOS 8(不到 1% 的设备)上 Universal Link 将无法工作。建议也支持 Custom URL Scheme 作为旧设备的 fallback,如果受众包括使用旧版本的用户。
iOS 16+ 变化:Apple 改进了 SwiftUI 的 Universal Link 处理。出现了新的 environment(\.openURL) 修饰符,具有延迟处理功能。iOS 16 还允许通过 SFSafariViewController 在应用中打开 Universal Link。对于 iOS 16 用户,建议完全转向 Universal Link 的 SwiftUI 处理,仅保留 AppDelegate 代码用于向后兼容。
常见问题
Universal Link 使用标准 HTTPS-URL,通过服务器上的文件进行验证。Custom URL Scheme 使用自定义协议(myapp://)无需验证,使其易被注册了相同方案的其他应用拦截。
文件 放在 HTTPS 服务器的根目录下 /.well-known/apple-app-site-association(无 .json 扩展名)。服务器必须返回 Content-Type: application/json。重要:无重定向,文件必须可直接访问。
主要原因:AASA 文件中 Team ID 或 Bundle ID 错误,文件无法通过 HTTPS 访问,重定向,Content-Type 错误,旧版本缓存。通过 Developer → Associated Domains Development 检查并重启设备重置缓存。
不可以 — Universal Link 需要托管了 apple-app-site-association 的 HTTPS 服务器。没有域名,Universal Link 无法工作。替代方案:Custom URL Scheme(不太安全)或具有自有域名的第三方服务(Branch、Firebase)。
不工作 — Universal Link 是 Apple 专有技术,适用于 iOS、iPadOS、watchOS 和 macOS。在 Android 上,其对应物称为 App Link(Android 6.0+),它使用 Digital Asset Links(assetlinks.json)代替 apple-app-site-association。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。