iOS 深度跳转(scheme、universal link)

网上看到这些文章,这里总结记录一下。

一、前言

首先,我们先来聊一下深度跳转时什么,有哪些方面可以运用。

点击wap页面按钮,唤起对应APP,并且跳转到了相对于的目标页面。比如:微信里浏览商品,点击打开APP,会自动唤起手机里的对应APP,并且会进入到你刚才浏览的商品详情页。

这就是比较直观的:深度跳转

上面的这种情况我们称之为:一键直达(或者一链拉起等,你怎么称呼你开心就好😊)

如果,手机没有安装京东APP,是会先去下载页面,待用户下载完后,点击打开APP依然会跳转到刚才浏览的商品页面

这种情况,我们可以称之为:场景还原

还要一些代码层面的应用也属于深度跳转的业务,比如:销售推广APP,用户点击了销售推广的APP下载链接,然后公司需要统计每个销售推广的量。

这种,就属于深度跳转与统计相关的一些内容了。如果公司产品使用了深度跳转,那必然会有些数据的统计。

对于 【一键直达】,我们可以用iOS里的最简单scheme实现,也可以使用universal link来实现,不过universal link稍微有些烦人的操作(特别是对于第一次使用的,很多坑等着你们🤕),而且universal link 还得要前端配合(个体开发没前端人员那就GG 了😔)。

对于 【场景还原】,我们就要和前端、后台配合开发,并且要定义各种逻辑、协议,客户端、前端、后台都需要有比较大的工作量。

对于不想踩坑、没那么多人力的可以选择一些比较靠谱的公司的SDK,比如:极光的魔链,还可以与极光的JPush 、JMessage 配合使用。

下篇文章为大家讲解如何使用 极光的魔链,已经 极光的魔链 的一些高级使用方法,和避免一些坑。

这篇我们先来了解下 scheme 和 universal link

Deeplink相关的技术,在wap中唤起app其实应用最最广泛的并不是Universal Link,而是直接Schema跳转

location.href = 'schema://xxxx'

并且一般各大APP都会给自己做一套路由体系,这样其实可以直接在schema头后面对接路由体系,做到一行schema定位打开任意App内功能界面(我就不详细扯路由的事了)

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation {

    if ([[url absoluteString] hasPrefix:@"schema://"]) {
         //路由
         //[object routeU:[url absoluteString]];
         return YES;
     }
}

        如果单纯为了实现deeplink -- 在WAP上打开App,并且传递来数据信息,定位App内的具体逻辑,那么Schema就够了,其实没必要上Universal Link.

  1. Schema相当于是很特殊的Url,他是schema://xxx这种样子,如果安装了APP才能支撑跳转这种Schema Url,如果没安装APP就没任何效果,
  2. 而Universal Link则是把普通url,长http://xxx.xxx.xxx/xxx 这样的Normal Url,如果安装了App,就能像Schema一样传递给App,延续App内逻辑,如果没装App,则还会继续在浏览器里跳转这个Normal Url

2.1 Schema无法判断是否安装App

一定会有这样的产品需求的:

  • 如果已经安装App,则打开App
  • 如果没有安装App,则前往下载App

        浏览器实际上是没有能力判断手机里是否安装了某个App的,所以聪明的程序员们选择了讨巧的方法

try {
    var appSchema = 'schema://xxxx';
    if ($.os.ios) {
        location.href = openNALocation; //location.href 打开schema
    }
    else {
        $('body').append('<iframe src="' + appSchema + '" style="display:none"></iframe>'); //iFrame 打开 schema
    } 
}catch (e) {}

//延迟1000秒
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`;//直接apk下载link
    } 
},1000)
  • 首先发起跳转Schema
    • 如果没安装App,会打开App失败,没效果
    • 如果安装App,会成功打开App
  • 延迟1000ms
    • 如果没安装App,Schema打开失败,等1000秒后会自动跳转
    • 如果安装App,App会打开,当前网页会被暂停,这延迟代码不会执行

        聪明的人会发现,这样有个风险,如果用户打开APP成功后,又手动切回浏览器,那么延迟1000ms的代码依然会执行,安卓会跳出下载apk包得提示,iOS会又再度跳到Appstore,但这个瑕疵也不是太大的问题,所以这种做法被普遍采用,运用在各种安装就跳转,不安装就下载的用户场景。

坑货来了😂 :安卓这么用挺好,iOS有个讨厌的弹框

如果用户没有安装App,那么他一定会经历2个事情

  • schema打开app,但是失败
  • 延迟后,跳转下载App

        在第一个环节,安卓上schema打开失败,没有任何反映,也没有任何提示,一切顺利,但是iOS就不同了。

schema会弹个可恶的跳转失败的框:

然后再延迟后弹跳转AppStore的框:

2.2 Schema被很多App禁止,比如微信手百

        Schema被广泛使用,从浏览器中唤起打开专门的App,但这并不被很多App认可,比如微信手机百度,他们本身除了浏览网页以外有其他的使用场景,所以站在微信/手百的角度,并不希望用户为了看一些分享和内容就跳出微信/手百的App,于是这些客户端拦截了Schema,使得所有Schema都无法生效。

        于是不得已,广大开发者只好针对,微信/手百,等特殊UA信息,展现出蒙层,引导用户用系统/外部浏览器打开

文章前面就说了,如果你单纯为了能让wap打开App,Schema就能做到了,Universal Link的意义则是把普通url,也赋予了能打开App的能力,而不必编写专门的Schema Url去唤起App

Schema 的2个弊端确实能通过Universal Link解决

  1. 不同于Schema,在没装App的时候,Universal Link他也是一个合法的url链接,浏览器可以正常跳转,因此不会出现在iOS上讨人厌的框
  2. Universal Link目前还没有基于iOS的UI/WKWebView的应用进行拦截,所以目前看还是能突破微信/手百的封锁。(以后,不好说啊~)

1.创建配置文件

新建一个名字为 apple-app-site-association 的纯文本文件,不要有任何后缀,文件内容为:

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "团队ID.软件BundleID",
                "paths": [ "限制的域名"]
            }
        ]
    }
}

       比如你团队 ID 是 6PA8SXXXXX,app 的 bundle id 是 com.hudongdong.blog,只在 访问 www.lazypig.net/app/lazypig… 链接时才显示顶部的用 App 打开,其他网站层次不显示,那么这个文件的内容就是:

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "6PA8SXXXXX.com.hudongdong.blog",
                "paths": [ "/app/lazypigquick/*"]
            }
        ]
    }
}

如果填了限制的 paths,在其他网站例如微信后台,填写 Universal Links 就是填写 www.lazypig.net/app/lazypig…

如果没有限制 paths,填写的是*,那么前端和后台填写的地址就是 www.lazypig.net/,全站的头部都显示用 App 打开,那么 paths 修改为 "paths": [ "*"] 即可。

apps 参数

apps 这个字段保持为空数组即可。

details 参数

details 是指定哪个页面用哪个 App 打开的数组,如果你有多个路径指定不同的 App,按照 paths 规则添加对应的 appIDpaths 即可。

paths 参数

这个 paths 路径的更多限制规则可以参考下面:

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

    然后将这个文件上传到网站根目录,或者在根目录新建一个名字为 .well-known 的子目录,然后把这个文件上传到这个子目录中。

跳转到APP store

    同样是在 universal-link.example.com 服务器里启动一个 nginx,用来处理当手机没安装 app 时跳转到 APP store 的操作

server {
  ...
  location /open-app/ {
      rewrite ^  https://apps.apple.com/cn/app/xxxxx;
  }
}

3.1 配置apple-app-association

究竟哪些的url会被识别为Universal Link,全看这个apple-app-association文件 Apple Document UniversalLinks.html

  • 你的域名必须支持Https
  • 域名根目录下放这个文件apple-app-association,不带任何后缀
  • 文件为json保存为文本即可
  • json按着官网要求填写即可

      怎么写json其实没啥可教的,满世界的文章都教你咋写了,我们看个例子,点下面的链接,你的浏览器就会自动把知乎的apple-app-association的json file给down下来

知乎的 apple-app-association 文件

大家自己去格式化一下这个json 内容,先只看 applinks对应的内容,其他内容这里先不讲解。

划重点

        有心人可能看到,知乎的Universal Link配置的是 oia.zhihu.com 这个域名,并且path配置的是,即所域名下所有路径都进行识别,也就是说,知乎的universal link,只有当你访问 https://oia.zhihu.com/xxxx,在移动端会触发Universal Link,而知乎正经的Urlhttps//www.zhihu.com/xxx是不会触发Universal Link的,知乎为什么制作,为什么不把他的主域名配置Universal Link,这是由于*Universal Link的一个大坑所致**(文章后面具体介绍这个坑)。

PS:

apple-app-association 你可以看完全了知乎的json file,会发现里面也不止是 universal link

苹果的一些其他功能都和apple-app-association有关,都需要配置这个文件,增加更多json字段信息

比如Hand off 还有一些跨Web&App的分享

测试是否正确

苹果官方提供了一个网站来测试你配置的域名apple-app-association是否正常work

search.developer.apple.com/appsearch

        这个网站有点SB,就是你用他测试不通过,其实Universal Link也可能是生效的,比如我把知乎的oia.zhihu.com输入进去,他就没感应到,认为没有。我搜索的时候,发现也有人发现了这个问题,反正可以当个参考。
        而且,好像第一次上传 apple-app-association 到域名根目录时,好像要48小时后才能正常使用,这个不知道大家是否遇到过,o((⊙﹏⊙))o

3.2 配置iOS App工程

我想略过这部分😔,很简单,很多教程·····

还是简单说一下吧。

  • 开发者中心证书打开Associated Domains
  • 工程配置Associated Domains
  • 将你apple-app-association所在域名配置进去
  • 给你的工程像Schema的OpenUrl一样,编写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; 
        [selfhandleUniversalLink:webUrl]; // 转化为App路由 
        } 
    return YES; 
}

3.3 Universal Link的基本运作流程

  • APP第一次启动 or APP更新版本后第一次启动
  • APP向工程里配置的域名发起Get请求拉取apple-app-association Json File
  • APP将apple-app-association注册给系统
  • 由任意webview发起跳转的url,如果命中了apple-app-association注册过的通用链
  • 打开App,触发Universal Link delegate
  • 没命中,webview继续跳转url

在你进行apple-app-association 以及 App工程的配置之后,整个Universal Link的运作流程完全由系统控制了

        整个Universal Link 开发其实非常简单,没几行代码就开发完成,完全写不了几行代码,就差不多搞定了,但是,但是,但是,还真是踩了几个坑····悲伤

4.1 跨域

这个坑是坑我最惨的😔

        前端开发经常面临跨域问题,恩Universal Link也有跨域问题,但不一样的是,Universal Link,必须要求跨域,如果不跨域,就不行,就失效,就不工作。(iOS 9.2之后的改动,苹果就这么规定这么设计的)

       这也是上面拿知乎举例子的时候重点强调的一个问题,知乎为什么使用oia.zhihu.com做Universal Link?

  • 假如当前网页的域名是 A
  • 当前网页发起跳转的域名是 B
  • 必须要求 B 和 A 是不同域名,才会触发Universal Link
  • 如果B 和 A 是相同域名,只会继续在当前WebView里面进行跳转,哪怕你的Universal Link一切正常,根本不会打开App

我直接拿知乎举例子看:

https://www.zhihu.com/oauth/redirect/social
知乎的一般网页URL都是www.zhihu.com域名,你在微信朋友圈看到了知乎的问题分享,如果copy url 你就能看到这样的链接

        微信里其实是屏蔽Schema的,但是你依然能看到大大的一个按钮App内打开,这确实就是通过Universal Link来实现的,但如果知乎把Universal Link 配在了www.zhihu.com域名,那么即便已经安装了App,Universal Link也是不会生效的。

        一般的公司都会有自己的主域名,比如知乎的www.zhihu.com,在各处分享传播的时候,也都是直接分享基于主域名的url,但为了解决苹果强制要求跨域才生效的问题,Universal Link就不能配置在主域名下,于是知乎才会准备一个oia.zhihu.com域名,专为Universal Link使用,不会跟任何主动传播分享的域名撞车,从而在任何活动WAP页面里,都能顺利让Universal Link生效。

简单总结一句话:

  • 只有当前webview的url域名,与跳转目标url域名不一致时,Universal Link 才生效

4.2 apple-app-association 文件覆盖

        我们业务机房的集群是大部门下几条业务线共用的,有一整套云服务系统来进行机房集群的管理,有统一的接入层进行分发。虽然是不同的产品线,不同的服务,但是共享分布式的机房进行运作的。

很多Universal Link的教学文章是这么写的

  • 将json命名为 apple-app-association 不要乱改名
  • 将file 上传到域名所在的服务器根目录下

        于是我就将我们文库的apple-app-association,上传到我准备的wenkuUniversal域名的所在机器的根目录下。(因为机房都是分布式的,所以其实就是upload的全部门下的很多机器上)

        同部门另一个产品线阅读后来也开始尝试Universal Link,也要把他们写好的apple-app-association上传到他们的yueduUniversal域名所在的机器的根目录下。

因为都是同样的文件名,又因为整个事业部机器实际上是共用的,因此就发生了覆盖。

解决办法

  • 共用同一个 apple-app-association

        因为apple-app-association的具体内容里有App 的Bundle ID在,因此可以简单的把2个json file 进行merge,你的App bundle 生效你的link,我的App bundle生效我的link

        但其实并不推荐,毕竟双方都要小心在更新的时候,不能覆盖对方,并且这样做也很不合理,apple-app-association也不止为universal link 一个feature工作,当面临跨app / web share 甚至hand off的时候,共用一个json file 还是有坑

  • 接入层分发不同json file

        2个产品线的link域名其实是不一样的,只不过恰巧这两个域名最重打到得机器是同一个或者说有重叠,因此产生了覆盖,完全可以将json文件保存成各自的名字,在接入层对域名进行分发

        最终App也是通过Get请求去拉取apple-app-association的,只要Get到,并且ssl安全性上符合要求(强制https)就没问题

隐藏的坑:apple-app-association被覆盖后如何更新

        我们线上已经work的Universal Link功能,突然有一天发现坏了,查了一圈最后查到被阅读覆盖了,那就修复呗,修复倒是没问题,问题在于修复后的universal link,用户必须重新安装一次app,才能重新work,这个太坑了啊

       所以关键是需要掌握apple-app-association的更新时机,反复重新杀APP重开完全没用,删了APP重装确实有用,但不可能让用户这么去做

        其实,每次App安装后的第一次Launch,会拉取apple-app-association,除此之外在Appstore每次App的版本更新后的第一次Launch,也会拉取apple-app-association。

        也就是说,一旦不小心因为意外apple-app-association,想要挽回又让那部分用户无感,App再发一个版本就好了。

        Universal Link 触发后打开App,这时候App的状态栏右上角会有文字提示来自XXApp,可以点状态栏的文字快速返回原来的AP

        如果用户点了返回微信,就会被苹果记住,认为用户并不需要跳出原App打开新App,因此这个App的Universal Link会被关闭,再也无效。

        想要开启也不是不行,让用户重新用safari打开,universal link的页面,然后会出现很像苹果smart bar的东西,那个东西点了后就能打开(我是看到的别人写的,我没亲自操作过)

为了统一WAP&APP,为了通用链接的效果

        我刚才提到,我们选择的Universal Link的域名其实是一个没有实际页面的域名,也就是说https://xxx.xxx.xxx/*这个url,如果没安装APP因此触发WebView继续跳转原地址,会直接404。处理很简单,重定向一下

最简单的就是把这个地址重定向到下载地址,打不开就直接到下载页面了。

五、iOS Universal Links 不起作用,几种可能

具体 Universal Links 的配置方法参见:
https://www.cnblogs.com/guoshaobin/p/11164000.html
写得很详细了。

5.1 查看一下,是否真的能被 Apple 访问到

到: https://search.developer.apple.com/appsearch-validation-tool/
这里输入自己的域名查一下

也可以使用命令

curl -v https://app-site-association.cdn-apple.com/a/v1/domain-name-here.com

看一下苹果真正拿到的数据是什么样的

后来找到原因:

5.2 可能原因1 本地未更新到

参考:iOS Universal Link(通用链接) - 简书 这篇文章上的

服务器上apple-app-site-association的更新不会让iOS本地的apple-app-site-association同步更新,即iOS只会在App第一次启动时请求一次,以后除非App更新或重新安装,否则不会在每次打开时请求apple-app-site-association。

重新安装APP。

有时也不起作用,那就做的彻底一点,每更改一次apple-app-site-association, 我重启一次iPhone,再重安装。

5.3 可能原因2

服务端的链接不符合,可能各种redirect导致链接地址不一致了

5.4 缓存原因

通过使用

curl -v https://app-site-association.cdn-apple.com/a/v1/domain-name-here.com

看一下,这里的结果,这个是安装好APP后,第一次启动时,取得的真正的文件的样子,一般有缓存不会马上更新。

六、apple-app-site-association CDN缓存问题,缓存时间

apple-app-site-association CDN | Apple Developer Forums

apple-app-site-association CDN | Apple Developer Forums

​​​​​​ios - apple-app-site-association file is not fetched from server but cached at apple? - Stack Overflow

七、总结

        scheme的弊端,以及universal link的使用和遇到的一些坑,都简单介绍介绍了。其实,深度跳转的运用不仅仅是这么简单,不仅仅是用于唤起APP 这么单一的。

        深度跳转可以做很多更为丰富的功能,比如文章开篇说的一链直达场景还原等。

参考:

iOS 唤起APP之Universal Link(通用链接) - Code.Rookie - 博客园

iOS 深度跳转(scheme、universal link)-极光社区

iOS Universal Links 不起作用,几种可能 - 简书

iOS 唤起 APP: Universal Link(通用链接) - 掘金

IOS H5页面通过universal-link(通用链接)打开APP | Laravel China 社区


版权声明:本文为yangyangye原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>