1.1 Universal link 是什么
Universal Link是苹果在WWDC上提出的iOS9的新个性之一。此个性相似于深层链接,并可能不便地通过关上一个Https链接来间接启动您的客户端利用(手机有装置App)。对比起以往所应用的URL Scheme,这种新个性在实现web-app的无缝链接时可能提供极佳的用户体验。
当你的利用反对Universal Link(通用链接),当用户点击一个链接是能够跳转到你的网站并取得无缝重定向到对应的APP,且不须要通过Safari浏览器。如果你的利用不反对的话,则会在Safari中关上该链接。在苹果开发者中能够看到对它的介绍是:
Seamlessly link to content inside your app, or on your website in iOS 9 or later. With universal links, you can always give users the most integrated mobile experience, even when your app isn’t installed on their device.
1.2 Universal link 的利用场景
应用Universal Link(通用链接)能够让用户在Safari浏览器或者其余APP的webview中拉起相应的APP,也能够在APP中应用相应的性能,从而来把用户引流到APP中。
这具体是一种怎么的情景呢?举个例子,你的用户safari外面浏览一个你们公司的网页,而此时用户手机也同时装置有你们公司的App;而Universal Link可能使得用户在关上某个详情页时间接关上你的app并达到app中相应的内容页面,从而施行用户想要的操作(例如查看某条新闻,查看某个商品的明细等等)。比方在Safari浏览器中进入淘宝网页点击关上APP则会应用Universal Link(通用链接)来拉起淘宝APP。
1.3 Universal link 跳转的益处
- 唯一性: 不像自定义的URL Scheme,因为它应用规范的HTTPS协定链接到你的web站点,所以个别不会被其它的APP所申明。另外,URL scheme因为是自定义的协定,所以在没有装置 app 的状况下是无奈间接关上的(在Safari中还会呈现一个不可关上的弹窗),而Universal Link(通用链接)自身是一个HTTPS链接,所以有更好的兼容性;
- 平安: 当用户的手机上安装了你的APP,那么零碎会去你配置的网站下来下载你上传上去的阐明文件(这个阐明文件申明了以后该HTTPS链接能够关上那些APP)。因为只有你本人能力上传文件到你网站的根目录,所以你的网站和你的APP之间的关联是平安的;
- 可变: 当用户手机上没有装置你的APP的时候,Universal Link(通用链接)也可能工作。如果你违心,在没有装置你的app的时候,用户点击链接,会在safari中展现你网站的内容;
- 简略: 一个HTTPS的链接,能够同时作用于网站和APP;
- 公有: 其它APP能够在不须要晓得你的APP是否装置了的状况下和你的APP互相通信。
- Universal link配置和运行
2.1 配置App ID反对Associated Domains
登录https://developer.apple.com/ 苹果开发者核心,找到对应的App ID,在Application Services列表里有Associated Domains一条,把它变为Enabled就能够了。
2.2 配置iOS App工程
Xcode 11.0版本
工程配置中相应性能:targets->Signing&Capabilites->Capability->Associated Domains,在其中的Domains中填入你想反对的域名,也必须必须以applinks:为前缀。563513413,不论你是大牛还是小白都欢送入驻
具体步骤如下图:
Xcode 11.0以下版本
工程配置中相应性能:targets->Capabilites->Associated Domains,在其中的Domains中填入你想反对的域名,必须以applinks:为前缀。
配置我的项目中的Associated Domains:
2.2 配置和上传apple-app-association
到底哪些的url会被辨认为Universal Link,全看这个apple-app-association文件Apple Document UniversalLinks.html
- 你的域名必须反对Https
- 域名 根目录 或者
.well-known
目录下放这个文件apple-app-association
,不带任何后缀 - 文件为json保留为文本即可
- json按着官网要求填写即可
apple-app-site-association
模板:
{
“applinks”: {
“apps”: [],
“details”: [
{
“appID”: “9JA89QQLNQ.com.apple.wwdc”,
“paths”: [ “/wwdc/news/”, “/videos/wwdc/2015/*”]
},
{
“appID”: “ABCD1234.com.apple.wwdc”,
“paths”: [ “*” ]
}
]
}
}
阐明:
appID:组成形式是 teamId.yourapp’s bundle identifier。如下面的 9JA89QQLNQ就是teamId。登陆开发者核心,在Account -> Membership外面能够找到Team ID。
paths:设定你的app反对的门路列表,只有这些指定的门路的链接,能力被app所解决。星号的写法代表了可识 别域名下所有链接。
上传指定文件:上传该文件到你的域名所对应的根目录或者.well-known目录下,这是为了苹果能获取到你上传的文件。上传完后,本人先拜访一下,看看是否可能获取到,当你在浏览器中输出这个文件链接后,应该是间接下载apple-app-site-association文件。
2.4 如何验证 Universal link 失效
- 能够应用iOS自带的备忘录程序,输出链接,长按链接,如果弹出菜单中有”在‘xxx’中关上”,即示意配置失效。
- 或者将要测试的网址在
Safari
中关上,在呈现的网页上方下滑,能够看到有在”xxx”利用中关上, 呈现菜单:
当点击某个链接,间接能够进咱们的app了,然而咱们的目标是要可能获取到用户进来的链接,依据链接来展现给用户相应的内容。
在AppDelegate
里中实现代理办法,官网链接:Handling Universal Links
Objective-C:
- (BOOL)application:(UIApplication )application continueUserActivity:(NSUserActivity )userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb])
{
NSURL *url = userActivity.webpageURL;
if (url是咱们心愿解决的)
{
//进行咱们的解决
}
else
{
[[UIApplication sharedApplication] openURL:url];
}
}
return YES;
}
Swift:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([Any]?) -> Void) -> Bool
{
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let incomingURL = userActivity.webpageURL,
let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
let path = components.path,
let params = components.queryItems else {
return false
}
print(“path = (path)”)
if let albumName = params.first(where: { $0.name == “albumname” } )?.value,
let photoIndex = params.first(where: { $0.name == “index” })?.value {
print(“album = (albumName)”)
print(“photoIndex = (photoIndex)”)
return true
} else {
print(“Either album name or photo index missing”)
return false
}
}
- Universal link遇到的问题和解决办法
3.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
是不是不太好了解,那间接拿知乎举例子
有心人可能看到,知乎的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的跨域的起因。
知乎的个别网页URL都是www.zhihu.com
域名,你在微信朋友圈看到了知乎的问题分享,如果copy url 你就能看到这样的链接
https://www.zhihu.com/question/22914651
微信里其实是屏蔽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失效。
跨域的另外一个益处是能够冲破微信跳转限度,反对微信无缝跳转到App.
简略一句话
只有以后webview的url域名,与跳转指标url域名不统一时,Universal Link 才失效
3.2 更新
apple-app-association的更新机会有以下两种:
- 每次App装置后的第一次Launch,会拉取apple-app-association
- Appstore每次App的版本更新后的第一次Launch,也会更新apple-app-association
所以重复从新杀APP重开齐全没用,删了App重装的确有用,但不可能让用户这么去做。也就是说,一旦不小心因为意外apple-app-association,想要挽回又让那局部用户无感,App再发一个版本就好了
3.3 Universal Link用户行为
Universal Link 触发后关上App,这时候App的状态栏右上角会有文字提醒来自XXApp,能够点状态栏的文字疾速返回原来的AP
如果用户点了返回微信,就会被苹果记住,认为用户并不需要跳出原App关上新App,因而这个App的Universal Link会被敞开,再也有效。
想要开启也不是不行,让用户从新用safari关上,universal link的页面,而后会呈现很像苹果smart bar的货色,那个货色点了后就能关上
- H5端的Universal Link业务部署
H5端的Universal Link跳转,从产品经理的角度看,须要满足以下2个需要:
- 如果已装置App,跳转对应界面
- 如果没装置App,跳转App下载界面
H5端部署 Universal Link示例:
router.use(‘/view’, function (req, res, next) {
var path = req.path;
res.redirect(‘https://www.xxx.com/view’ + path + ‘?xxx=xxx’);
});
整个成果就是
- 跳转
https://www.xxx.com/view/*
- 已装置App
- 关上App触发handleUniversalLink
- 走到/view/分支,拼接浏览页路由跳转
- 未装置AppWebView
- 原地跳转
https://
`www.xxx.com`/view/*
- 命中服务器的重定向逻辑
- 重定向到
https://
`www.xxx.com`/view/*
- 关上相应的H5页面
- 附:关上App过渡页.html示例源码
<!DOCTYPE html”>
<html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
<meta name=”viewport” content=”width=device-width,minimum-scale=1.0″>
<body bgcolor = “#FFEEDD”>
<input type=”text” id=”acti”>
<p>下载页面ios 点击链接</p>
</body>
<script type=”text/javascript”>
var timeout;
function open_iOS_App() {
if (isWeiXin()) {
open_App();
}else{
open_App();
timeout = setTimeout(‘open_itunes()’, 3000);
};
}
function open_Android_App() {
if (isWeiXin()) {
open_android_weixin();
}else{
open_App_Android();
};
}
function open_android_weixin() {
var acti = document.getElementById(“acti”).value;
var linkUrl = “xxxxxxxxxxxxx://utils?action=sendIntent¶ms=” + acti;
var yingyongbaoUrl = “http://a.app.qq.com/o/simple?pkgname=com.xxxx.xxxxx&android_schema=”
- encodeURI(linkUrl);
window.location = yingyongbaoUrl;
}
function open_App() {
var ver = (navigator.appVersion).match(/OS (d+)_(d+)_?(d+)?/);
ver = parseInt(ver[1], 10);
if(ver<9)
{
if (isWeiXin()) {
open_weixin_App();
}else{
open_itunes();
}
} else{
var activity = document.getElementById(“acti”).value;
document.getElementById(“aaa”).href = “https://joeychang.me/s.html?params=”+ encodeURI(activity);
// alert(document.getElementById(“aaa”).href);
// alert(activity);
document.getElementById(“aaa”).click();
}
}
function open_App_Android() {
var acti = document.getElementById(“acti”).value;
window.location = “intent://xxxxxxxxx?params=”+ acti +”#Intent;package=com.xxxx.xxxxx;scheme=xxxxxxx;launchFlags=268435456;end;”;
}
function open_itunes() {/ 关上app store /
window.location=”http://itunes.apple.com/cn/app/idxxxxxxxx”;
}
function open_weixin_App() {/ 关上腾讯利用宝 间接跳转 /
var acti = document.getElementById(“acti”).value;
window.location=”http://a.app.qq.com/o/simple.jsp?pkgname=com.xxxx.xxxxx&activity=” + acti;
}
/*
判断是否是微信浏览器
*/
function isWeiXin(){
var ua = window.navigator.userAgent.toLowerCase();
if(ua.match(/MicroMessenger/i) == ‘micromessenger’){
return true;
}else{
return false;
}
}
function linktoApp() {
var queryStr = decodeURI(window.location.search.substr(1));
var ua = navigator.userAgent.toLowerCase();
if (queryStr.indexOf(“}”) >= 0 && queryStr.indexOf(“{“) >= 0) {
var activity = queryStr.substring(queryStr.indexOf(“{“), queryStr.lastIndexOf(“}”) + 1);
document.getElementById(“acti”).value = activity;
if (/iphone|ipad|ipod/.test(ua)) {
// IOS
} else if (/android/.test(ua)) {
// Android
open_Android_App();
} else {
// 其余浏览器
window.location=”http://a.app.qq.com/o/simple.jsp?pkgname=com.xxxxxxx.xxxxxxxx”;
}
} else {
document.getElementById(“acti”).value = “参数格局谬误”;
}
}
// 用js实现在加载实现一个页面后主动执行一个办法
/用window.onload调用myfun()/
window.onload=linktoApp;//不要括号
</script>
</head>
<body>
</body>
</html>
发表回复