Universal Link (通用链接) - 唤起App

9,946 阅读10分钟

背景

我们在开发中经常会遇到从微信、从网页、从自家App中打开另外一个App的需求,并且要求跳转到指定页面,并继续完成后续业务。唤起App三种方案对比:

1、URL Scheme

a、配置
截屏2021-05-31 下午8.36.55.png

b、调用 截屏2021-05-31 下午8.47.50.png

优点:
十分简单,在plist文件中配置一个scheme,点击短信中的scheme或者将scheme输入到浏览器中就可以直接跳到App中。

缺点:
当手机上没有安装App时,短信中点击scheme会没有反应,浏览器中会访问失败;而且这种方式不太安全;对于唤起后跳转到App指定页面的这种方式拓展性不强。(而且在微信和QQ中这种方式是被禁用的)

2、写一个网页

H5唤起APP指南

在短信中点击链接,直接跳转到网页(引导下载页),在网页中判断是iOS端还是安卓端,然后自动跳转对应的scheme,如果手机上安装了App,则直接跳到App,如果没有安装App,则停留在当前网页,在网页上有一个去下载的按钮,点击按钮可以去苹果商店下载App。

在h5页面中判断是否成功唤起:
如果从h5页面唤起了App的话,页面就会进入后台运行,会触发h5页面的 visibilitychange 事件。如果触发了,则表明页面被成功唤起。

3、Universal Link (通用链接) - 最低支持iOS9

现在App都是最低支持iOS9了,可以直接用Universal Link替换URL Scheme了。

优点:

  • 唯一性: 它使用标准的 https 链接到你的web站点,所以它不会被其它的App所声明。
  • 安全: 只有你自己才能上传文件到你网站的根目录,所以你的网站和你的App之间的关联是安全的。iOS会去你的网站上去下载你上传上去的说明文件(这个说明文件声明了你的App可以打开哪些类型的https链接)。
  • 可变: Universal Links本身是一个 https 链接,当用户手机上没有安装你的App的时候也能够工作。此时点击链接会跳转到safari中展示你网页。
  • 简单: 一个URL链接,可以同时作用于网站和App。
  • 私有: 其它App可以在不需要知道你的App是否安装了的情况下和你的App相互通信.

.

一、Universal Link的基本运作流程

App第一次启动、更新版本后第一次启动,都会自动下载apple-app-site-association配置文件

  • 下载 - App向工程里配置的域名发起Get请求拉取配置好存放在https服务器上的配置文件
  • 注册 - App将配置文件注册给手机系统
  • 命中 - 由任意h5、或其他App发起的跳转url,如果命中了配置文件注册过的通用链接,就打开App
  • 没命中 - 直接跳转url链接对应的h5页面

.

二、Universal Link配置 - 开发步骤

1、开发者账号配置:

打开证书页面,找到Identifiers下App IDs下自己的证书;勾选Associated Domains 截屏2021-05-24 下午3.00.45.png

2、Xcode配置:

在Capabilities选项下打开Associated Domains;在Domains中填入你想支持的域名(存放打开App的文件的https服务器地址,必须applinks:开头) 该列表限制为不超过20到30个。 截屏2021-05-24 下午2.58.10.png

配置完后,Xcode中会自动生成一个.entitlements文件,专门存放Associated Domains的配置信息。 如果你的项目中有多个环境每个环境的域名不同,你可以拷贝多个.entitlements文件,每个环境对应一个.entitlements。

3、apple-app-site-association文件配置:

配置并上传配置文件到服务器中该域名的根目录下,可以用GET请求可以获取到这个配置文件。需注意的是文件不需要加后缀,部分服务器无法访问无后缀的文件, (文件的未压缩大小不得大于128 KB,数组中字典的顺序决定了系统在寻找匹配项时所遵循的顺序)

apple-app-site-association文件名固定,不能修改,不能加后缀; 必须支持https且不能重定向

{
    "applinks":{
        "apps":[],
        "details":[
            {
                "appID":”9JXXXXXXNQ.com.XXXX.test",
                "paths":["/dgtest/*"] //只要链接中包含/dgtest/就可以唤起app
            }
        ]
    }
}

a、配置

appID:组成方式是 teamId + bundle identifier。如上面的 9JXXXXXXNQ就是teamId。登陆开发者中心,在Account -> Membership里面可以找到Team ID。

paths:设定你的App支持的路径列表,只有这些指定的路径的链接,才能被App所处理。paths是按照顺序匹配路径的,所以高优先级路径要放在前面。

指定网站路径的规则:

  • 使用"*"指定整个网站
  • 包含特定的网址(例如"/wwdc/news/")以指定特定的链接
  • 附加*到特定的网址(例如"/videos/wwdc/2015/*")以指定网站的一部分
  • 除了用于*匹配任何子字符串之外,您还可以?用于匹配任何单个字符。您可以将两个通配符合并在一个路径中,例如"/foo/*/bar/201?/mypage"
  • 如果要指定不应作为通用链接处理的区域,请在路径字符串的开头添加“ NOT ”(注意有一个空格 "NOT /wwdc/2010/*")

配置完可以验证一下json格式是否正确:www.bejson.com

b、上传

上传文件到HTTPS Web服务器的根目录或.well-known子目录。 这是为了苹果能获取到你上传的文件。上传完后,自己先访问一下,看看是否能够获取到文件,当你在浏览器中输入这个文件链接后,应该是直接下载apple-app-site-association配置文件。

4、实现接收方法:

Appdelegate中实现continueUserActivity:代理方法,并做打开的相应处理(不实现也不影响唤起功能)

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler{

    NSString *url = [userActivity.webpageURL absoluteString];
    url = [url stringByRemovingPercentEncoding];
    //进行自定义处理
    
    return YES;
}

5、调用

["*"]          调起https://mbankshare.XXXX.cn就可以启动App 
["/dgtest/*"]  调起https://mbankshare.XXXX.cn/dgtest/*就可以启动App

在任何地方打开你的网址,系统命中后会直接跳转到你的app,没有命中会跳转到对应的网页;

如果在Safari中打开网址,在出现的网页上方下滑,可以看到有在”XX”应用中打开,点击“打开”按钮直接跳转App。 IMG_0399.jpg

.

三、拓展

最新官方文档 偶然间看到苹果新的文档,关联文件格式有比较大的改动,这里简单做个记录:

构建关联文件后,将其放置在站点的 /.well-known/ 目录下。(文档没有提到放在根目录下,不知道是否还支持)

从macOS 11和iOS 14开始,应用程序不再将apple-app-site-association文件请求直接发送到Web服务器。而是将这些请求发送到专用于关联域的Apple管理的内容交付网络(CDN)。

{
  "applinks": {
      "details": [
           {
             "appIDs": [ "ABCDE12345.com.example.app", "ABCDE12345.com.example.app2" ],
             "components": [
               {
                  "/": "/buy/*",
                  "comment": "匹配路径以 /buy/ 开头的任何URL"
               },
               {
                  "/": "/help/*",
                  "?": { "articleNumber": "????" },
                  "comment": "匹配路径以/help/开头的任何URL,该URL有一个为'articleNumber'的查询项,值为4个字符"
               },
               {
                  "/": "/help/website/*",
                  "exclude": true,
                  "comment": "匹配路径以 /help/website/ 开头的任何URL,并指示系统不要将其作为通用链接打开"
               },
               {
                  "#": "universal_links",
                  "comment": "匹配任何片段等于 universal_links 的URL"
               }
             ]
           }
       ]
   },
   "webcredentials": {
      "apps": [ "ABCDE12345.com.example.app" ]
   },

    "appclips": {
        "apps": ["ABCED12345.com.example.MyApp.Clip"]
    }
}

第一层级多了这两个:
Web Credentials 是手机上的密码(证书)填充功能
App Clips 是轻应用功能

details词典仅适用于applinks服务类型;其他服务类型不使用它。components是字典的阵列,其提供模式匹配的URL的组件。

.

注意:

  • 如果要对没有path的域名进行支持(如:www.163.com ), 在json文件的paths中用通配符’*’是不行的,需要在paths数组中加入’/’进行匹配。
  • 存放apple-app-site-association文件的服务器域名地址必须是HTTPS的,并且SSL证书必须通过苹果信任。苹果支持的HTTPS证书列表
  • 即使页面打开是404,只要网址格式符合规则,都可以命中并成功唤起
  • App处于运行或者后台时点击分享出去的链接,会跳转到应用里面以及对应的界面;但是杀死App,再点击分享出去的链接只是单独的唤醒App并没有执行continueUserActivity这个方法(可能是在启动的时候做了其他操作,从而影响了在continueUserActivity方法中的处理)
  • 网页不能重定向:网页重定向会导致唤起失效

.

四、无法唤起 - 解决方案:

1、当我所有配置都配完,发现无法唤起,检查发现把英文的引号"打成了中文的引号“。

2、改好后再了几遍确认配置无误后,依然还是无法唤起App。

于是尝试了如下方法:

  • 关闭代理;
  • 清空safari缓存;
  • safari不要开启隐私模式;
  • 重启手机;
  • 重新安装App;

结果:依然无法唤起

3、在苹果验证网站验证,一直提示:“XXX" isn't a valid webpage URL. Please check your URL and try again.(即使正常的地址也会提示这个,不知道什么情况)

4、继续用HTTP状态查询工具(查重定向等)排查问题,发现http状态码是202。 截屏2021-06-01 上午11.05.09.png

最后找后台同事沟通了老半天,最终发现原来是我们服务器加了安全设备检测工具,对网络进行了拦截导致的,苹果发起请求的时候被判定为非安全,所以导致被拦截了。所以apple-app-site-association文件下载一直是失败的,唤起App也就不会成功了 T.T
服务器去掉拦截后正常了。

5、最开始我是匹配的链接中的部分关键字,当我改成匹配全链接的时候发现唤起异常了。经过各种尝试,发现是链接中有特殊字符#导致的唤起异常。额外试了一下,只要字符串里面包含? % # 等特殊字符就会识别异常无法唤起。

有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了。编码的格式为:%加字符的ASCII码,即一个百分号%,后面跟对应字符的ASCII(16进制)码值。例如 空格 的编码值是%20

URL中的特殊字符 :
+     URL 中+号表示空格%2B
空格  URL中的空格可以用+号或者编码%20
/     分隔目录和子目录%2F
?     分隔实际的URL和参数%3F
%    指定特殊字符%25
#     表示书签%23
&     URL 中指定的参数间的分隔符%26
=     URL 中指定参数的值%3D

方案一:下载链接不变,在配置路径时用?替换特殊字符(结果:失败)

方案二:下载链接不变,在配置路径时用“编码”替换对应的特殊字符(结果:失败)

方案三:去掉下载链接中的特殊字符(结果:成功)

.

五:iOS14 唤起异常

iOS 通用链接(Universal Link)配置(适配iOS14)

配置文件已经放到了.well-known目录下;并且配置文件已使用新版的格式,但是在iOS14上还是唤起异常。

(待完善。。。已处理好这个问题的大佬请在下方留下宝贵的意见)

.

参考:

iOS微信授权登录+Universal Link(通用链接)
iOS唤起App之Universal Link(通用链接)
Universal Link苹果官方文档

json验证地址(查apple-app-site-association格式是否正确)
HTTP状态查询工具
苹果验证网站

如发现遗漏或者错误,请在下方评论区留言。