iOS无第三方实现Universal Links延迟深度链接方案咨询
自定义Apple延迟深度链接实现方案
核心逻辑
延迟深度链接的本质是在用户点击Universal Links时暂存链接信息,App安装完成后再从后端拉取该信息。因为Apple默认不会在App安装后自动传递原Universal Links,所以需要通过自定义的设备标识关联存储来实现。
1. 前端(Universal Links落地页)处理流程
当用户点击Universal Links且App未安装时,落地页需要完成以下操作:
- 生成唯一设备标识(可使用UUID,存入localStorage避免重复生成)
- 将原Universal Links地址、设备标识、推广活动参数等提交到后端接口存储
- 跳转至携带设备标识参数的App Store链接
// 落地页JS示例 window.addEventListener('load', () => { // 获取或生成设备标识 let deviceId = localStorage.getItem('dl_device_id'); if (!deviceId) { deviceId = crypto.randomUUID(); // 现代浏览器支持,兼容旧环境可自行实现UUID生成 localStorage.setItem('dl_device_id', deviceId); } // 原Universal Links地址(当前页面URL) const originalLink = window.location.href; // 提交至后端存储 fetch('https://your-api-domain.com/save-deeplink', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ device_id: deviceId, original_link: originalLink, campaign: getCampaignParams() // 自行实现提取UTM等推广参数 }) }).then(() => { // 跳转带device_id的App Store链接 window.location.href = `itms-apps://itunes.apple.com/app/idYOUR_APP_ID?mt=8&dl_device_id=${deviceId}`; }); });
2. 后端存储接口实现
后端需要提供两个核心接口:
- 存储接口:接收设备标识、原链接、推广参数,存入数据库(需设置过期时间,比如7天,清理无效数据)
- 查询接口:根据设备标识返回对应的深度链接信息,返回后标记为已处理避免重复拉取
数据库表字段参考:
| 字段名 | 类型 | 说明 |
|---|---|---|
| device_id | VARCHAR(64) | 唯一设备标识(主键) |
| original_link | TEXT | 原Universal Links地址 |
| campaign_params | JSON | 推广参数(UTM等) |
| created_at | DATETIME | 创建时间 |
| expired_at | DATETIME | 过期时间(7天后) |
| is_processed | BOOLEAN | 是否已被App拉取处理 |
3. App端获取深度链接逻辑
App启动时,优先从启动参数中解析设备标识,若未获取到则尝试从Keychain读取(App卸载后Keychain数据可保留),然后调用后端查询接口获取原链接并处理。
// AppDelegate示例 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 从启动参数中解析设备标识 if let url = launchOptions?[.url] as? URL { parseDeviceId(from: url) } // 从Keychain读取设备标识(可选,用于调试或参数丢失场景) if let savedDeviceId = KeychainManager.shared.getDeviceId() { fetchDeepLink(deviceId: savedDeviceId) } return true } // 解析App Store跳转URL中的设备标识 private func parseDeviceId(from url: URL) { guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let deviceId = components.queryItems?.first(where: { $0.name == "dl_device_id" })?.value else { return } // 存入Keychain备用 KeychainManager.shared.saveDeviceId(deviceId) // 请求后端获取深度链接 fetchDeepLink(deviceId: deviceId) } // 请求后端获取深度链接 private func fetchDeepLink(deviceId: String) { guard let url = URL(string: "https://your-api-domain.com/get-deeplink?device_id=\(deviceId)") else { return } URLSession.shared.dataTask(with: url) { data, _, error in guard let data = data, error == nil else { return } do { let response = try JSONDecoder().decode(DeepLinkResponse.self, from: data) if let originalLinkStr = response.originalLink, let originalLink = URL(string: originalLinkStr) { DispatchQueue.main.async { // 处理原链接,比如跳转对应页面 self.handleUniversalLink(originalLink) } // 通知后端标记为已处理 self.markDeepLinkAsProcessed(deviceId: deviceId) } } catch { print("解析深度链接失败: \(error)") } }.resume() } // 标记深度链接为已处理 private func markDeepLinkAsProcessed(deviceId: String) { guard let url = URL(string: "https://your-api-domain.com/mark-deeplink-processed?device_id=\(deviceId)") else { return } URLSession.shared.dataTask(with: url).resume() } // 自定义响应模型 struct DeepLinkResponse: Codable { let originalLink: String? let campaignParams: [String: String]? }
4. 调试场景处理
用Xcode直接启动App时,无法获取App Store跳转的参数,可通过以下方式测试:
- Debug模式下添加测试入口(比如设置页按钮),手动输入测试用的device_id
- 提前在后端存入测试链接,Debug时自动拉取预设的测试device_id
5. 隐私合规提示
- 设备标识不要存储敏感信息,需在App隐私政策中说明用途
- 若使用IDFA关联用户,需申请广告追踪权限,并在隐私政策中披露
内容的提问来源于stack exchange,提问作者Faraz Raza
相关产品推荐
相关产品推荐

