具体细节能够看javascript
但Univeral Link 仍是能够学习学习看看的~毕竟几遍在Safari下还有个好处,能够干掉schema跳转的那个难看的错误弹框前端
-- 以上来自2018.1.8修改java
前言:ios
文章会适当说一些如何开发iOS上的universal link,但相似的文章太多了一艘一大堆,每篇都介绍的挺清楚,所以也不是重点git
本文更加会侧重从前端的角度,将整个universal link 部署应用到wap app中的一些策略和一些问题解决办法程序员
其实整个Universal Link没啥难的,真正上线过Universal link的人这些应该都趟过一遍了,本文主要是咱们team去应用Universal link的时候一些文档沉淀和记录github
Deeplink相关的技术,在wap中唤起app其实应用最最普遍的并非Universal Link,而是直接Schema跳转web
location.href = 'schema://xxxx'
复制代码
而且通常各大APP都会给本身作一套路由体系,这样其实能够直接在schema头后面对接路由体系,作到一行schema定位打开任意App内功能界面(我就不详细扯路由的事了)json
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
if ([[url absoluteString] hasPrefix:@"schema://"]) {
[[WKDispatcher sharedInstance] operationObjectFromRouteURL:[url absoluteString]];//路由
return YES;
}
}
复制代码
若是单纯为了实现deeplink -- 在WAP上打开App,而且传递来数据信息,定位App内的具体逻辑,那么Schema就够了,其实不必上Universal Link,Schema至关因而很特殊的Url,他是schema://xxx
这种样子,若是安装了APP才能支撑跳转这种Schema Url,若是没安装APP就没任何效果,而Universal Link则是把普通url,长http://xxx.xxx.xxx/xxx
这样的Normal Url,若是安装了App,就能像Schema同样传递给App,延续App内逻辑,若是没装App,则还会继续在浏览器里跳转这个Normal Url
必定会有这样的产品需求的:
浏览器其实是没有能力判断手机里是否安装了某个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)
复制代码
聪明的人会发现,这样有个风险,若是用户打开APP成功后,又手动切回浏览器,那么延迟1000ms的代码依然会执行,安卓会跳出下载apk包得提示,iOS会又再度跳到Appstore,但这个瑕疵也不是太大的问题,因此这种作法被广泛采用,运用在各类安装就跳转,不安装就下载
的用户场景。
安卓这么用挺好,iOS有个讨厌的弹框
若是用户没有安装App,那么他必定会经历2个事情
在第一个环节,安卓上schema打开失败,没有任何反映,也没有任何提示,一切顺利,可是iOS就不一样了。
schema会弹个可恶的跳转失败的框
而后再延迟后弹跳转AppStore的框
Schema被普遍使用,从浏览器中唤起打开专门的App,但这并不被不少App承认,好比微信
,手机百度
,他们自己除了浏览网页之外有其余的使用场景,因此站在微信/手百的角度,并不但愿用户为了看一些分享和内容就跳出微信/手百的App,因而这些客户端拦截了Schema,使得全部Schema都没法生效。
因而不得已,广大开发者只好针对,微信/手百,等特殊UA信息,展示出蒙层,引导用户用系统/外部浏览器打开
开篇就说了,若是你单纯为了能让wap打开App,Schema就能作到了,Universal Link的意义则是把普通url,也赋予了能打开App的能力,而没必要编写专门的Schema Url去唤起App
Schema 的2个弊端确实能经过Universal Link解决
不一样于Schema,在没装App的时候,Universal Link他也是一个合法的url连接,浏览器能够正常跳转,所以不会出如今iOS上讨人厌的框
Universal Link目前尚未基于iOS的UI/WKWebView的应用进行拦截,因此目前看仍是能突破微信/手百的封锁。(之后,很差说啊~)
相似的话题,随便搜搜Universal Link能搜到一大堆,我这里就略微多啰嗦两句,通常各大教程里会反复说的,我尽可能一带而过,多说点我遇到的坑
究竟哪些的url会被识别为Universal Link,全看这个apple-app-association文件 Apple Document UniversalLinks.html
apple-app-association
,不带任何后缀怎么写json其实没啥可教的,满世界的文章都教你咋写了,咱们看个例子,点下面的连接,你的浏览器就会自动把知乎的apple-app-association
的json file给down下来
划重点
有心人可能看到,知乎的Universal Link配置的是 oia.zhihu.com
这个域名,而且对这个域名下好比/answers /questions /people 等urlpath进行了识别,也就是说,知乎的universal link,只有当你访问 https://oia.zhihu.com/questions/xxxx
,在移动端会触发Universal Link,而知乎正经的Urlhttps//www.zhihu.com/questions/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-v…
这个网站有点SB,就是你用他测试不经过,其实Universal Link也可能不生效的,好比我把知乎的oia.zhihu.com
输入进去,他就没感应到,认为没有。我搜索的时候,发现也有人发现了这个问题,反正能够当个参考
#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;
[self handleUniversalLink:webUrl]; // 转化为App路由
}
return YES;
}
复制代码
恩比较千篇一概,我很少说了
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其实真要只是开发完成,彻底写不了几行代码,就差很少搞定了,不过还真是踩了几个坑
前端开发常常面临跨域问题,恩Universal Link也有跨域问题,但不同的是,Universal Link,必需要求跨域,若是不跨域,就不行,就失效,就不工做。(iOS 9.2以后的改动,苹果就这么规定这么设计的)
这也是上面拿知乎举例子的时候重点强调的一个问题,知乎为何使用oia.zhihu.com
作Universal Link?
是否是不太好理解,那直接拿知乎举例子
知乎的通常网页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生效。
简单一句话
咱们业务机房的集群是大部门下几条业务线共用的,有一整套云服务系统来进行机房集群的管理,有统一的接入层进行分发。虽然是不一样的产品线,不一样的服务,可是共享分布式的机房进行运做的。
不少Universal Link的教学文章是这么写的
apple-app-association
不要乱更名因而我就将咱们文库的apple-app-association,上传到我准备的wenkuUniversal域名的所在机器的根目录下。(由于机房都是分布式的,因此其实就是upload的所有门下的不少机器上)
同部门另外一个产品线阅读后来也开始尝试Universal Link,也要把他们写好的apple-app-association上传到他们的yueduUniversal域名所在的机器的根目录下。
由于都是一样的文件名,又由于整个事业部机器其实是共用的,所以就发生了覆盖。
解决办法
由于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 仍是有坑
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的东西,那个东西点了后就能打开(我是看到的,我没亲自操做过)
知乎的apple-app-association能够看到里面有一大堆的WAP的URL,好比/answers /questions /people等,知乎都将它一一映射到App得对应界面里,问题/回答/人详情页。这是由于知乎的WAP站和APP的功能几乎是一致的。所以知乎的Universal Link的使用方式,能够说是很经典的遵循着苹果的原始设计初衷通用连接
,将wap url,变成通用url,一样的url,对应着2个跳转,web跳转/app跳转,可是他们是同一个功能。
咱们产品线面临的状况不同,咱们的产品线文库,他的WAP和APP功能差别很是大,能够说除了文档阅读页/view,WAP与APP都有这个功能,其余的功能WAP是WAP的,APP是APP的,形态和场景都有明显差别。除了/view这个功能,咱们能够按着通用连接
的设计,将APP阅读页跳转,与WAP阅读页跳转进行统一。其余时候Universal Link对于咱们业务来讲就是一个更强大的Schema(突破旧Schema局限的=),他只须要跳转到APP,他没有合法的WAP Url可让浏览器在没有安装App的状况下继续跳转。
咱们的Universal Link就像知乎同样,没有选择咱们的主域名,而是选了一个彻底没在WAP上有任何页面和流量的域名,咱们的apple-app-association是这么写的
{
"appID": "xxxxxx.xxx.xxx.xxxxx",
"paths":[ "/view/*",
"/_iosuniversallink/*"]
},
复制代码
咱们的AppDelegate中具体handleUniversalLink的逻辑是这么写的
- (BOOL)handleUniversalLink:(NSURL *)url {
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];
NSString *host = components.host;
if ([host isEqualToString:@"xxx.xxx.xxx"]) { //host判断,虽然没啥意义
if (pathComponents.count >= 3) {
//地址匹配+页面跳转
NSString *router;
if ([pathComponents[1] isEqualToString:@"view"]) {
router = @"xxx";//生成打开APP阅读页的专属Router
} else if ([pathComponents[1] isEqualToString:@"_iosuniversallink"]) {
router = @"xxx";//解析出APP能识别的任意路由,
}
if (router && router.length > 0) {
[[WKDispatcher sharedInstance] operationObjectFromRouteURL:router];//不管是阅读页路由仍是任意路由,发起跳转
}
return YES;
}
}
return NO;
}
复制代码
能够看出来我只打开了这个域名下https://xxx.xxx.xxx/view/*
和 https://xxx.xxx.xxx/_iosuniversallink/*
2个Universal Link Path.对没错,不像知乎那么多。
/view/*
后面的*直接是阅读页ID,用于快速生成阅读页路由,发起跳转/_iosuniversallink/*
后面的*其实应该填写的是咱们App已经设计好的路由字符串,识别出路由字符串后,发起跳转其实能够看出来/_iosuniversallink是彻底包含/view的,由于APP阅读页自然也是包含在咱们的路由规则内的,只要这里有路由策略,扩展力已经足够支持任意APP页面了,所以apple-app-association只配置了2个,可是若是有计划外的特殊case,大不了更新一下,也没多大事。
我刚才提到,咱们选择的Universal Link的域名实际上是一个没有实际页面的域名,也就是说https://xxx.xxx.xxx/view/*
这个url,若是没安装APP所以触发WebView继续跳转原地址,会直接404。处理很简单,重定向一下
router.use('/view', function (req, res, next) {
var path = req.path;
res.redirect('https://wk.baidu.com/view' + path + '?st=1#1');
});
复制代码
整个效果就是
https://xxx.xxx.xxx/view/*
https://xxx.xxx.xxx/view/*
https://wk.baidu.com/view/*
这就是为啥明明/_iosuniversallink
是彻底包含/view
能力的,但仍是要把/view/
单独处理的缘由,为的是实现WAP与APP的统一设计,为了通用连接
这个初衷
一样的道理,https://xxx.xxx.xxx/_iosuniversallink/*
这个url,也没有实际的页面,若是不进行重定向,也会直接返回404,所以看一眼重定向的代码
router.use('/_iosuniversallink', function (req, res, next) {
var redirecturl = 'https://wk.baidu.com/topic/naiosappstore';
res.redirect(redirecturl);
});
复制代码
解释一下wk.baidu.com/topic/naios…就是咱们为iOS下载App准备的专门的WAP单页面,这个页面打开后会自动延迟500ms,发起跳转appstore
整个效果就是
https://xxx.xxx.xxx/_iosuniversallink/*
https://xxx.xxx.xxx/_iosuniversallink/*
https://wk.baidu.com/topic/naiosappstore
这个设计看起来就是完美解决了PM得需求
解决了旧Schema模式下的弊端问题:
setTimeout
的Trick方式简单的说,这样设计的初衷就是,我不为了通用连接
这一目的来使用Universal Link,来统一WAP&APP的URL跳转,我就为了把Universal Link当作增强版Schema来使用