iOSer 必知必会的深度链接技术——WWDC2019 更新

2019-07-09 14:34:10 +08:00
 MobService

iOSer 作为移动开发者中的一员,不得不说深度链接在当下这个“流量”时代已经成为我们的必修课了,那么什么是深度链接呢?简单的说就是,可以通过一个简单的“链接”,打开 App 并直接进入该 App 中的内容页。前提是该手机上已安装该 App,且该 App 需要支持深度链接。例如:在 Safari 里看到的澎湃新闻 App 的某一篇新闻 “中国又一新的世界遗产...” 点击下面滚动 Banner 上的“打开 App ”按钮便可直接进入澎湃新闻 App (已安装)中对应的新闻页面:

澎湃新闻 1-1

可见在移动端采用深度链接技术,极大的省去了用户打开 App、再搜索内容或者点击某处进入指定页面等繁琐的操作,直接点击网页上的打开按钮即可一键到达 App 内的指定页面。

一、iOS 上深度链接的由来 在介绍深度链接是怎么来的之前,有一个基础概念需要和大家同步一下:

SandBox (沙盒) SandBox (沙盒)是苹果官方规定的 iOS 系统强制应用程序只能够读取应用程序内部数据,不可以访问其他应用信息数据的一种机制。

在 iOS 系统的设备中每一个 App 都有自己的储存空间; App 只能访问自己沙盒目录下的内容,不能访问其它存储空间的内容; 应用程序的数据请求需要经过权限检测,检测不通过则不执行; 为什么使用沙盒? SandBox (沙盒)是安全体系中的一种机制,从而苹果公司在设计 iOS 系统时,考虑到应用之间的信息安全,对应用程序的访问权限设置了限制。

SandBox (沙盒)的弊端 使用沙盒机制后 App 之间不能相互访问进行通信,从而使得 App 成为一个个的信息孤岛。(弱小可怜又无助)

如何解决 SandBox (沙盒)问题? 不能说苹果的初衷怎么样,但是带来的问题是显而易见的,那么其实苹果早在 2010 年 iOS 4 的时候就已经意识到 App 信息孤立的问题了,所以推出了 URL Scheme 技术,此技术使得 iOS 系统可以通过特定的 URL 方式传递参数给另外一个 App。例如:iOSer://userid=123456&name=sands。

不得不承认,这个技术确实解决了当初比较棘手的问题,但是在日新月异的今天,URL Scheme 的诟病也日渐显著,比如想要实现两个 App 之间的跳转则需要兼并开发,再比如 URL Scheme 能够打开 App 的前提是已经安装了 App,如果没有安装则一定会报错,相信下面的错误大家一定都见的不少,更重要的是现在越来越多的浏览器已经不再支持 URL Scheme 了,这必然让我们不得不另辟蹊径。

IMG_0080

相信苹果也是深刻的意识到了 URL Scheme 已经不再是长久之计了,所以苹果在 2015 年 iOS 9 中隆重推出了 Universal Links (通用链接) 。

二、深度链接解决的问题 Universal Links(通用连接) 一种能够通过点击传统 HTTPS 链接 来 启动 App 或者 打开对应网站 的技术。

通过唯一的网址, 不需要特别的 URI Scheme 就可以链接一个特定 App 里面的视图 。比如:一个 App 分享内容到微信,用户在微信内置浏览器中看到 H5 页面内容,然后用户点击触发 Universal Links 链接后,即可直接打开 App 内相同的页面内容。( PS: 由于微信 6.5 版本之后做了 屏蔽操作 ,导致无法直接打开 App 了,但这并不影响系统引导。)

NOTE

Universal links let users open your app when they tap links to your website within WKWebView and UIWebView views and Safari pages, in addition to links that result in a call to openURL:, such as those that occur in Mail, Messages, and other apps.

When a user is browsing your website in Safari and they tap a universal link to a URL in the same domain as the current webpage, iOS respects the user ’ s most likely intent and opens the link in Safari. If the user taps a universal link to a URL in a different domain, iOS opens the link in your app.

For users who are running versions of iOS earlier than 9.0, tapping a universal link to your website opens the link in Safari.

首先,这是一种标准的 HTTPS 链接,为什么要强调这一点呢?因为它解决了一个很核心的问题,即使你的设备上没有安装 App,那么点击该链接也不会出现上面“ Safari 无法打开该网页,因为网址无效”的报错,它可以当作普通网页正常被访问,为用户体验层面的提升提供了更多的可能。

其次,通过 Universal Links 跳转到自己 App 从而进行通讯的方式不需要两个 App 之间的兼并开发,别人的 App 里不需要为打开自己的 App 做任何配置,只需要自己开发配置好自己的 App 和网页即可,不管自己的网页是在哪一个 App 里被打开,网页和 App 之间实现的 Universal Links 都一直有效。

最后,使用这种方式还有几个细节优势点,比如省去了 URL Scheme 跳转 App 前的系统确认提示框,相比之下更直接,另外,App 未安装时点击之后直接访问网页,一方面解决了 URL Scheme 在浏览器层面不可知的成功或者失败,同时还能够呈现给用户网页内容,引导用户下载或是进行网页操作,不管从哪一层面来说,这都是一种完胜 URL Scheme 的方式。

三、如何实现 iOS 上的深度链接 添加 Universal Links 的支持很简单。你只需要三步即可实现:

创建一个 apple-app-site-association 文件,文件内容是关于你的 App 能够处理的 URLs 的 JSON 数据 上传上面创建好的这个 apple-app-site-association 文件到你的支持 HTTPS 的 Web 服务器。你可以将这个文件放到你服务器的根目录下或者 .well-known 的子文件夹里 在你的 App 里配置、处理 Universal Links 创建和上传 Association 文件 为了在你的网站和 App 之间创建一个安全的连接,你建立了它们之间的信任关系。这个信任关系的建立分两步:

一个是你添加到你的网站的 apple-app-site-association 文件 另一个是你添加到你的 App 的 com.apple.developer.associated-domains 权限(这一部分在下面的 准备处理 Universal Links 的 App 中介绍) 在您的 apple-app-site-association 文件中,您可以指定网站中应作为通用链接处理的路径以及不应作为通用链接处理的路径。保持路径列表相当短,并通过正则通配符的方式来匹配更多的路径集。如下是一个 apple-app-site-association 文件的示例,该文件标识了应作为通用链接处理的三个路径。

{ "applinks": { "apps": [], "details": [ { "appID": "9JA89QQLNQ.com.mob.moblink", "paths": [ "/moblink/news/", "/videos/moblink/2019/"] }, { "appID": "ABCD1234.com.mob.moblink", "paths": [ "" ] } ] } } 注意

不要 在 apple-app-site-association 文件名后面添加 .json 后缀 。

apps:这个 key 必须存在,并且对应 value 必须是一个空数组 details:这个 key 对应的 value 是一个包含一个或多个字典的数组,其中每一个字典都对应一个网站支持的 app,另外,数组中字典的顺序决定了系统查询匹配的顺序,所以你可以指定 app 只处理你网站的部分路径 appID:这个 key 对应的 value 是 Team ID 后面拼接上 Bundle ID,这个很重要,千万不能错 paths:这个 key 对应的 value 是指定你的网站中哪些路径是需要(或者不需要) app 处理的字符串数组。 不需要匹配的 path 则在对应路径前面加 NOT 即可,例如:"paths": [ "/moblink/news/", "NOT /videos/moblink/2010/", "/videos/moblink/201?/"] 由于系统会顺序查找路径规则,一旦发现规则相匹配时则停止查找,所以你应该将优先级高的写在优先级低的后面。如果将前面的示例改为: "paths": [ "/moblink/news/", "/videos/moblink/201?/", "NOT /videos/moblink/2010/"] 则数组中最后一条规则就没有用了,因为它一定会被前一条规则拦截掉。

在 apple-app-site-association 文件中有很多种方式来指定网站路径。你可以:

用 * 来指定你的整个网页都支持 包含一个特殊的 URL,例如 /moblink/news 来指定部分链接支持 在特殊 URL 后面加 * ,例如 /videos/moblink/2017/* 来指定一组链接支持 注意 paths 数组中的路径字符串是大小写敏感的,一定要注意区分大小写。

创建 apple-app-site-association 文件后,将其上传到 HTTPS Web 服务器的根目录或 .well-known 子目录。该文件需要通过 HTTPS 直接访问-无需重定向-位于 https://<domain>/apple-app-site-association 或 https://<domain>/.well-known/apple-app-site-association。接下来,你需要在你的 App 中处理 Universal Links。</domain></domain>

准备处理 Universal Links 的 App Universal Links 使用了两项技术:第一个是在 Web 浏览器和 App 之间启用 Handoff 的相同机制,第二个是共享 Web 凭据。关于这两项技术可以参考官方 Web Browser – to – Native App Handoff 和 Shared Web Credentials Reference。当用户点击一个 Universal Link 时,iOS 系统启动你的 App,并且带了一个你可以查询到你的 App 是通过什么方式被启动的 NSUserActivity 对象参数到你的 App 里。

要在你的 App 里支持 Universal Links,只需要做如下两步:

添加一个指定的你的 App 支持的域名权限 更新你的 AppDelegate 当接收到 NSUserActivity 对象时作出适当的响应 在你 App 的 com.apple.developer.associated-domains 权限里,包含了所有你的 App 想要当作 universal link 处理的域名列表。在 Xcode 的项目主页中,选择 Capabilities tab 页,然后打开 Associated Domains,以 applinks: 开头添加上你的 App 支持的域名,例如:applinks:z.t4m.cn 、applinks:*.ulml.mob.com

注意:这个域名权限列表最多添加 20 到 30 个。

15625698347787

配置好 Associated Domains 之后,就要在 AppDelegate.m 中实现 HandOff 的 UIApplicationDelegate 方法以便你的 App 能够接收到 universal link 并进行适当的处理。

四、WWDC2019: What's New in Universal Links 时隔多年,在刚结束没有多久的 WWDC 2019 中,又再一次提到了 Universal Links,虽然 整个 session 不长,也就不到 20 分钟的时间,但足以体验苹果一直在做这件事儿。

首先,划一下重点:Universal Links 之前只支持在 iOS 和 tvOS 平台上,现在全面登陆到 macOS 平台上了,无论你是用 UIKit 还是 AppKit 开发的 Mac App 都可以使用 Universal Links。

这一次 WWDC2019 Universal Links 主要的变化有两点:

apple-app-site-association 文件支持精细化地址匹配写法 全面支持 macOS 系统 OverView 总览 Universal Link 的基本运作机制 通过在 XCode 的 App 配置中配置了相关信息以及安全域名指定 通过在 Https only 的安全域名上部署一个配置 apple-app-site-association 文件 apple-app-site-association 文件中配置上丰富的 website 与 app 的链接信息 在 website 与 app 之间建立起了安全有效的握手机制 实现 website 的 url 与 app 的直接联动 相对于自定义的 schemes 来说 Universal Link 能带给你更安全更流畅的体验 补充:schemes 上最令人诟病的就是 app 劫持,很多 app 会冒充注册知名大厂的 app scheme 从而拦截调常规的大厂 scheme 唤起,这一点在 Universal Link 就能很好的避免。

apple-app-site-association 文件精细化匹配 15625733635308

上图是 MobLink 这款提供深度链接功能的 SDK 的 apple-app-site-association 文件老的写法示例。

WechatIMG399

这是新的写法,支持了更多更强的配置能力。

apps 这个字段只有在 iOS 上有用,tvOS/macOS 这个字段可以忽略 details 字段结构大幅度变化 以前是字典 appID 为 Key,现在是数组,并且支持 appIDs 这样的 key,可以一套配置适配多个 appID,大幅度减少工作量 新增 components 字段,可以进一步约束 Universal Link 的生效条件 可以通过 / 来配置支持的 path 格式条件 可以通过 # 来配置支持的锚点条件 可以通过 ? 来配置支持的字段条件 exclude 是排除字段,符合这个条件的 Universal Link 不生效 Universal Links 网络服务的配置建议 在部署网络服务,配置 apple-app-site-association 文件的时候,苹果建议还要遵循一下几点 Tips

必须支持 https: 必须是正规证书,不能使用自定义证书。

URL 使用 ASCII 编码 因为在 URL 中会使用很多符号诸如 # ?% ,因此要使用 ASCII 编码。

减少 apple-app-site-association 文件的大小 减少大小有助于在拥堵的网络条件下,更顺畅的生效 Universal Link。如果你的应用面向不通的国家,针对不同的国家有不同的配置文件,可以通过国家标记符来分别配置,从而让用户只下载最精简的 JSON 文件。

apple-app-site-association 文件的下载和更新策略: 当 App 安装到设备的时候,会从配置域名上下载这个 JSON 文件到本地,从而根据配置生效相关的功能。并且设备上下载的这个 JSON 文件会不定期的更新(亲测过,App 发布版本更新的时候也会准确触发 JSON 文件的更新)。

使用 Smart Banner 网页中的 Smart Banner 与 Universal Links 一起配合使用,可以有效的引导未安装 app 的用户前往下载,已安装 app 的用户就可以顺畅的进行跳转

Universal Links 支持 macOS 在 XCode 中配置 Universal Links

WechatIMG398

开发 Universal Links 的代码,支持 macOS:

// Configuring Your App func application(_ application: Application, continue userActivity: NSUserActivity, restorationHandler: @escaping ([ UserActivityRestoring]?) -> Void) -> Bool { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL, let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return false } for queryItem in components.queryItems ?? [] { … return true } 在支持 macOS 的情况下,appDelegate 的 Universal Link 的处理也是这个 delegate,区别只是 UIApplication 变为 NSApplication。

由于这个 delegate 会由很多其他的快捷方式触发,不仅仅由 Universal Link 触发,所以通过 NSUserActivityTypeBrowsingWeb 来判断来源,剩下的就是标准的 url 处理了。

macOS 下的 Universal Links 工作机制与 iOS 下存在一定的差异

默认打开网页,会提示用户是否需要打开 app 远程登录下无法生效 Appstore 签名 macApp 可以在下载 App 后立刻生效 开发者签名的 macApp 必须在用户运行过一次后生效 在 mac app 中打开一个 Universal Link

// UIApplication UIApplication.shared.open(url, options: [ .universalLinksOnly: true ]) { … }

// NSWorkspace let configuration = NSWorkspace.OpenConfiguration() configuration.requiresUniversalLinks = true NSWorkspace.shared.open(url, configuration: configuration) { … }

2142 次点击
所在节点    Apple
5 条回复
shenfu1991
2019-07-09 18:27:52 +08:00
微信 qq 微博 大多数都屏蔽了,现在搞这个,基本上没多大意义。
fenddddddda
2019-07-09 20:00:49 +08:00
gogogogogo
2019-07-09 20:46:29 +08:00
如何解决 1 楼的问题?
well666
2019-07-09 21:00:29 +08:00
@shenfu1991 这个也真是...
dcty
2019-07-10 08:53:21 +08:00
讲真,非常讨厌这个功能,所以在 Android 上我都不用 chrome 了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/581329

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX