WWDC2019之 universal link 更新

WWDC2019中有很多session提到了 universal link,比如 new in authentication

Universal link 是一种提升用户体验的方式,它主要用于当用户点击一个指向你的网站链接。如果设备上安装了你的应用就可以直接打开应用;如果没有安装的话,就用safari打开链接。它有如下的特点:

  1. 链接唯一性:之前有某个应用拦截了某大厂的schema,而 universal link 不会出现这个问题
  2. 安全性: iOS 会检查app对应的服务器上的某个文件来判断是否允许app打开链接;这个文件只有开发者才能创建,因此是安全的
  3. 灵活性: 即使app没有安装,点击了链接也能工作
  4. 简单: 一个链接既适用于网页也适用于app
  5. 隐私性: 其它app只需要知道链接就可以和你的app通信

WWDC2019中介绍了uniersal link 更新的部分,但基本的实现步骤没有改变,下面简单介绍一下iOS上的实现。

实现步骤

主要有三个步骤:

  1. 准备文件名为: apple-app-site-association 的 JSON文件
  2. 将 JSON 文件放置到部署了 https 的服务器 .well-known 子目录
  3. 在App中实现相关的协议

WWDC2019中特地提到不需要对JSON文件签名。

JSON 关联文件 association file

为了保证app和网页之间的安全链接,需要:

  1. 在网站上配置名为 apple-app-site-association 的文件,注意不需要后缀
  2. 在App中的entitlement文件添加 com.apple.developer.associated-domains

先来看下网站配置文件格式:

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "9JA89QQLNQ.com.apple.wwdc",
                "paths": [ "/wwdc/news/", "NOT /videos/wwdc/2015/*", "/videos/wwdc/201?/*"],
                "appIDs": ["9JA89QQLNQ.com.apple.wwdc", "ABCDE12345.com.example.app"],
                "components": [
                    {
                        "/": "/path/*/filename",
                        "#": "*fragment",
                        "?": {"widget": "?*", "grommet": "please"},
                        "exclude": true
                    }
                ]
            },
            {
                "appID": "ABCD1234.com.apple.wwdc",
                "paths": [ "*" ]
            }
        ]
    }
}

details 中则是app的详细信息,数组顺序决定了app匹配的优先级。其中,appID 的组成是

team id or app ID prefix + app_bundle_ID

paths是一组URL规则,允许或者不允许app打开的链接。而在WWDC2019中,JOSN数据更新了规则,新增 appIDs 和 components。 appIDs 是一个数组,允许把多个 appID 添加到一块; componets 也是一个数组,数组元素是一系列模式匹配规则;匹配的优先级取决于数组中的顺序。
另外,网站必须配置了 https,而且得是浏览器能识别的根证书签署的,而不能是自定义的证书。

App中的设置

首先配置entitlement文件,把网站的link添加进去,这一步通过Xcode的Capability操作。可以添加20~30个域名,域名的格式:

applink: domain_name

domain_name 支持 * 通配符,支持子域名。匹配的时候,越详细的子域名,拥有更高的优先级。此外,对于 domain.com,规则 *.domain.com 是不能匹配的。
如果域名中包含非 ASCII 字符,那么需要使用 PonyCode 转译,如:

上海海.example.中国 ->
xn--fhqz97e.example.xn--fiqs8s

当用户点击了链接并且满足iOS唤醒App的情形时,iOS会生成 NSUserActivity 实例,并发送给 App Delegate,因此需要在程序中实现响应的协议, 如:

func application(_ application:UIApplication, 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 ?? [] {
        // your handler
    }
    return true
}

其它说明

Universal link现在支持 macOS,TVOS,上面只对iOS说明。鉴于不同系统的人机交互有差异,因此不同系统上存在差异。在macOS上的差异主要有:

  1. 点击链接,默认从浏览器打开
  2. App必须按照在本地的磁盘上
  3. 最好使用从App Store下载的app:因为下载后,系统会自动去你的服务器下载JSON文件
  4. Developer ID-Signed的app必须运行过一次,才能很好地支持 universal link