字节笔记本
2026年2月22日
iOS 深度跳转完全指南:Scheme 与 Universal Link 详解
本文详细介绍 iOS 深度跳转的两种实现方式:Scheme 和 Universal Link,对比它们的优缺点,并深入讲解 Universal Link 的开发配置和常见坑点。
前言
深度跳转是指点击 WAP 页面按钮,唤起对应 APP 并跳转到相应目标页面的技术。常见的应用场景包括:
- 一键直达:点击"打开 APP"按钮,直接唤起应用并进入指定页面
- 场景还原:未安装 APP 时引导下载,安装后仍能保持之前的浏览上下文
对于一键直达,可以使用 iOS 的 Scheme 或 Universal Link 实现;对于场景还原,则需要与前端、后台配合开发。
Scheme VS Universal Link
Scheme 的基本原理
Deeplink 相关的技术中,在 WAP 中唤起 APP 最广泛使用的是 Scheme 跳转:
location.href = 'schema://xxxx'一般各大 APP 都会给自己做一套路由体系,可以直接在 Scheme 头后面对接路由体系:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString*)sourceApplication
annotation:(id)annotation {
if ([[url absoluteString] hasPrefix:@"schema://"]) {
// 路由处理
return YES;
}
}Scheme 的弊端
1. 无法判断是否安装 App
浏览器没有能力判断手机是否安装了某个 App,因此采用讨巧的方法:
try {
var appSchema = 'schema://xxxx';
if ($.os.ios) {
location.href = appSchema;
} else {
$('body').append('<iframe src="' + appSchema + '" style="display:none"></iframe>');
}
} catch (e) {}
setTimeout(function () {
if ($.os.ios) {
location.href = 'https://itunes.apple.com/us/app/idxxxxxxx?mt=8';
} else {
location.href = 'https://xxx.xxx.xxx/xxx/xxx.apk';
}
}, 1000);原理:
- 首先发起跳转 Scheme
- 如果没安装 App,会打开失败,无效果
- 如果安装 App,会成功打开 App
- 延迟 1000ms
- 如果没安装 App,等延迟后会自动跳转下载
- 如果安装 App,当前网页被暂停,延迟代码不会执行
iOS 的问题:安卓这么用挺好,但 iOS 有个讨厌的弹框。如果用户没有安装 App,会经历:
- Scheme 打开 App 失败,弹出错误提示框
- 延迟后,跳转 AppStore,再次弹出确认框
2. 被很多 App 禁止
Scheme 被广泛使用,但并不被很多 App 认可,比如微信、手机百度等。这些客户端拦截了 Scheme,使得所有 Scheme 都无法生效。开发者只好针对这些特殊 UA 展现蒙层,引导用户用系统浏览器打开。
Universal Link 的优势
Universal Link 把普通 URL(http://xxx.xxx.xxx/xxx)也赋予了能打开 App 的能力:
- 如果安装了 App,能像 Scheme 一样传递给 App
- 如果没装 App,会继续在浏览器里跳转这个 Normal URL
- 不同于 Scheme,在没装 App 时不会弹出讨人厌的错误框
- 目前还没有基于 iOS 的 UI/WKWebView 的应用进行拦截,能突破微信/手百的封锁
Universal Link 开发
配置 apple-app-association
究竟哪些 URL 会被识别为 Universal Link,全看这个 apple-app-association 文件:
要求:
- 域名必须支持 HTTPS
- 域名根目录下放这个文件
apple-app-association,不带任何后缀 - 文件为 JSON 格式
示例(知乎的配置):
- 知乎的 Universal Link 配置在
oia.zhihu.com域名 - Path 配置为
*,即所有路径都进行识别 - 只有访问
https://oia.zhihu.com/xxxx才会触发 Universal Link - 主域名
https://www.zhihu.com/xxx不会触发
测试工具:苹果官方提供 App Search API Validation Tool 来测试配置是否正常。
配置 iOS App 工程
- 开发者中心证书打开 Associated Domains
- 工程配置 Associated Domains
- 将 apple-app-association 所在域名配置进去
- 编写 App 被唤醒后的处理逻辑:
#pragma mark Universal Links
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity*)userActivity
restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL *webUrl = userActivity.webpageURL;
[self handleUniversalLink:webUrl]; // 转化为 App 路由
}
return YES;
}Universal Link 运作流程
- APP 第一次启动或更新版本后第一次启动
- APP 向工程里配置的域名发起 Get 请求拉取 apple-app-association
- APP 将 apple-app-association 注册给系统
- 由任意 WebView 发起跳转的 URL,如果命中了注册过的通用链接
- 打开 App,触发 Universal Link delegate
- 没命中,WebView 继续跳转 URL
Universal Link 采坑指南
坑点 1:跨域问题
这是最大的坑! Universal Link 必须要求跨域,如果不跨域就不生效(iOS 9.2 之后的改动)。
规则:
- 假如当前网页的域名是 A
- 当前网页发起跳转的域名是 B
- 必须要求 B 和 A 是不同域名,才会触发 Universal Link
- 如果 B 和 A 相同,只会继续在当前 WebView 里面跳转
知乎的解决方案:
- 主域名
www.zhihu.com用于正常网页访问 - 单独准备
oia.zhihu.com域名专为 Universal Link 使用 - 这样在任何活动 WAP 页面里,都能顺利让 Universal Link 生效
坑点 2:apple-app-association 文件覆盖
问题:多个产品线共用机房集群时,如果都上传同名文件到根目录,会发生覆盖。
解决方案:
- 共用同一个文件:将多个 JSON 合并,因为内容里有 App 的 Bundle ID 区分
- 接入层分发:不同域名在接入层分发不同的 JSON 文件
隐藏的坑:文件被覆盖后,用户必须重新安装 App 或更新 App 版本才能恢复。
坑点 3:用户行为导致失效
Universal Link 触发后打开 App,状态栏右上角会有文字提示"来自 XX App",点击可快速返回原 App。
问题:如果用户点了返回,苹果会记住这个行为,认为用户不需要跳出原 App,因此这个 App 的 Universal Link 会被关闭,再也无效。
恢复方法:让用户重新用 Safari 打开 Universal Link 页面,点击 Smart Bar 上的按钮重新开启。
统一 WAP & APP 体验
Universal Link 的域名通常没有实际页面,如果没安装 App 会 404。处理方法是将这个地址重定向到下载页面。
总结
| 特性 | Scheme | Universal Link |
|---|---|---|
| 实现复杂度 | 简单 | 较复杂 |
| 判断是否安装 App | 不能 | 能(通过跨域) |
| 微信/手百支持 | 被拦截 | 支持 |
| 未安装 App 体验 | 有错误弹框 | 正常跳转网页 |
| 需要 HTTPS | 否 | 是 |
| 需要前端配合 | 否 | 是 |
Scheme 和 Universal Link 各有适用场景。如果只是想简单实现 WAP 打开 App,Scheme 就够了;如果需要更好的用户体验,特别是要在微信等环境中使用,Universal Link 是更好的选择。
深度跳转的运用不仅限于唤起 APP,还可以实现一链直达、场景还原等更丰富的功能。
原文来源:极光社区 - 好好搬砖