iOS学习

iOS Universal link 入门指南

2020-10-10  本文已影响0人  iOS丶lant

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 跳转的好处

2. Universal link配置和运行

2.1 配置App ID支持Associated Domains

登录https://developer.apple.com/ 苹果开发者中心,找到对应的App ID,在Application Services列表里有Associated Domains一条,把它变为Enabled就可以了。

image

2.2 配置iOS App工程

Xcode 11.0版本

工程配置中相应功能:targets->Signing&Capabilites->Capability->Associated Domains,在其中的Domains中填入你想支持的域名,也必须必须以applinks:为前缀。

具体步骤如下图:

image image image

Xcode 11.0以下版本

工程配置中相应功能:targets->Capabilites->Associated Domains,在其中的Domains中填入你想支持的域名,必须以applinks:为前缀。

配置项目中的Associated Domains:

image

2.2 配置和上传apple-app-association

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

apple-app-site-association模板:


{

 "applinks": {

 "apps": [],

 "details": [

 {

 "appID": "9JA89QQLNQ.com.apple.wwdc",

 "paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"]

 },

 {

 "appID": "ABCD1234.com.apple.wwdc",

 "paths": [ "*" ]

 }

 ]

 }

}

如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

说明:

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 生效

image

当点击某个链接,直接可以进我们的app了,但是我们的目的是要能够获取到用户进来的链接,根据链接来展示给用户相应的内容。1012951431,不管你是大牛还是小白都欢迎入驻

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

 }

}

3. Universal link遇到的问题和解决方法

3.1 跨域

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

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

是不是不太好理解,那直接拿知乎举例子

有心人可能看到,知乎的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

image

微信里其实是屏蔽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重开完全没用,删了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的东西,那个东西点了后就能打开

4. H5端的Universal Link业务部署

H5端的Universal Link跳转,从产品经理的角度看,需要满足以下2个需求:

H5端部署 Universal Link示例:


router.use('/view', function (req, res, next) {

 var path = req.path;

 res.redirect('https://www.xxx.com/view' + path + '?xxx=xxx');

});

整个效果就是

5. 附:打开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>下载页面<a href="javascript:;" onclick="open_iOS_App()">ios 点击链接</a></p>

 </body>

 <a href="" id="aaa"></a>

 <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>

上一篇 下一篇

猜你喜欢

热点阅读