iOS 中的 Deferred Deep Linking(延迟深度链接)
http://www.cocoachina.com/ios/20160105/14871.html
Deep Linking
其实 deep linking 并不是一个新名词,在 web 开发领域,区别于指向首页的链接(http://tech.glowing.com/),deep linking 是指向具体内容页的链接(http://tech.glowing.com/cn/advices-to-junior-developers/)。在移动开发领域,deep linking 则是指 mobile app 在 handle 特定 URI 的时候可以直接跳转到对应的内容页或触发特定逻辑,而不仅仅是启动 app。比如 dianping://shopinfo?id=1859284,如果你的手机上装了大众点评的话点击这个链接可以直接跳转到商铺页面。这样做的好处主要有:
在 web 和 app 的切换过程中保留上下文
App 间带上下文切换(用于实现 app 间参数的传递,如授权协议,分享 API 等)
Web 页可以被搜索引擎索引,可以通过 SEO 增加访问量从而提高 app 下载量和开启率
目前处理 deep linking,主要有两种方式:
Custom URL Scheme
在 universal links 出现之前的很长一段时间里,iOS 上主要通过 custom URL scheme 来实现 deep linking,以及 app 间的通信。
在 info plist 里设置了自定义 URL后,handle URL 的入口是 app delegate 方法 application:openURL:sourceApplication:annotation:
(iOS 9 开始被 deprecate)或 application:openURL:options:
(iOS 9 引入,但如果没有实现这个方法,在 iOS 9 上还是会向前兼容 call 老方法,所以一般还是实现老方法)。
1
2
3
4
5
6
|
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { BOOL handled = NO; // code to handle the URL return handled; } |
一个比较完整的 NSURL
可以包含以下部分:scheme://user:password@host:port/path?query#fragment
。但对于 deep linking 来说大部分时候只需要 scheme://host/path?query
。有时候会省去 path
部分,把 host
直接作为 command,如上文提到的点评的 link;也有些 app 会省去 query
部分,用 path
传参,更接近 RESTful API 的风格。这取决于具体业务逻辑复杂程度以及 handler 的实现方式。有一点需要注意的是,规范的 URL 是 percentage encoded 的,所以取出来的参数需要用 stringByReplacingPercentEscapesUsingEncoding:
或 stringByRemovingPercentEncoding
(iOS 7+)方法 decode。反之,拼 URL 的时候应该使用 stringByAddingPercentEscapesUsingEncoding:
或 stringByAddingPercentEncodingWithAllowedCharacters:
(iOS 7+)方法 encode。
在 iOS 7+ 上处理 query
的时候也可以配合使用 NSURLComponents
类。
具体 handle URL 的时候,对于需要处理的业务逻辑较少的 app 来说,可以简单地通过字符串比较来区分业务逻辑。对于业务逻辑相对复杂,特别是在跨团队共同维护 URL handler 的时候,则需要引入 router 来分发请求。关于 router 已经有很多文章涉及,GitHub 上也有很多开源代码可供参考或使用,比如:
具体选型或自己实现 router 的时候主要考虑一些问题比如:用 code 注册还是配置文件;是否需要去中心化;如何传参;以 view controller 还是 block (closure) 为单位来注册 handler;是否需要像淘宝一样做 web 版的 failover 等等……这里不再展开。
相关文档:Using URL Schemes to Communicate with Apps
Universal Links
Apple 在 iOS 9 上引入了 universal links,相较 custom URL scheme,universal links 有以下好处:
Custom URL scheme 因为是自定义的协议,所以在没有安装 app 的情况下是无法直接打开的,而 universal links 本身是一个 HTTP/HTTPS 链接,所以有更好的兼容性。
不同的 app 是可以定义相同的 custom URL scheme 的,所以会存在抢占或冲突的问题,而 universal links 是从 server 查询由哪个 app 打开的,所以不存在上述问题。
Universal links 支持从其他 app 的 MKWebView 或 UIWebView 中跳转到目标 app。
Universal links 本身可以被搜索引擎索引。
Universal links 的具体实现可以参考官方文档:Support Universal Links。简单来说你需要:
添加一个
apple-app-site-association
文件到你的网站来描述 URL 和 app 的关联。添加
com.apple.developer.associated-domains
entitlement 来指定要从哪些域名查询 universal links support。在 app delegate 的
application:continueUserActivity:restorationHandler:
方法中 handleuserActivity.webpageURL
。
处理 URL 本身的方法跟前面处理 custom URL 类似,不再赘述。
Deferred Deep Linking
顾名思义,deferred deep linking 是指用户打开一个 web page 的时候并没有安装对应的 app,希望用户在安装 app 以后可以 deep link 到对应内容。这里有三个需要解决的问题:
判断是否已经安装了 app,如果已经安装了直接 deep link 到 app,否则跳转 App Store。
用户匹配(user matching),如何把一个 install 对应到某一次 web page view 或者某一次 click。
Deep linking
问题 1
以前在使用 custom URL 的时候一般用类似这样的一段 JS 处理:
1
2
3
4
|
setTimeout( function () { }, 250); |
这是因为在 iOS 9.2 以前,Safari 里是否用 app 打开 custom URL 的提示是 blocking JS 的,所以如果用户同意用 app 打开链接以后就不会跳转 App Store,反之,用户选择取消或者并没有安装 app 的时候,会跳转 App Store。iOS 9.2 Apple 做了一个更新就是这个提示不再 block JS,所以无论如何都会跳转 App Store。因此现在会推荐使用 universal links 来实现这样的逻辑,对于需要强制安装 app 后才能浏览的内容,可以提供一个直接跳转 App Store 的中转页面,如果装了 app,iOS 会自动跳转到 app 内处理。
问题 2
这曾经是个老大难的问题,受系统所限,在 iOS 上很难追踪到一个安装的来源,但是这样的需求又很多,主要的场景有:
追踪广告效果
追踪用户推荐/邀请链接
在 app 内保持网页浏览的上下文,如登录信息,购物车等
对于这个问题,在 iOS 9 以前常见的做法是猜,没错,就是用猜的。在访问特定页面或点击特定链接的时候记录用户特征,如 IP,系统版本,手机型号,语言等等。然后在打开 app 的时候发送这些特征到服务器,查询一段时间内(如 1 小时内)有可以匹配的用户点击过的链接,然后处理这个链接。这样做的缺点很明显,因为是通过特征模糊匹配的,所以很容易匹配不到或匹配到错误的上下文。但是其实大部分第三方服务会从不同来源收集更多信息,所以这个准确率其实比想象中高很多,尤其是在打开了 IDFA 的情况下。
这个问题却在 iOS 9 引入 SFSafariViewController
以后得到了很好的解决,因为 SFSafariViewController
和 Safari 的 cookies 是互通的!所以理论上可以做到 100% 的 match。解决方案也很简单,本地生成一个 UUID
并通过一个隐藏的 SFSafariViewController
传回给 server,server 就可以把这个 UUID
跟之前的 session 对应起来,然后通过一般的 API call 查询更多跟这个 session 有关的信息。具体的 code 可以参考 Branch SDK 的实现。
问题 3
上个章节已经提到,不再赘述,只是处理 URL 的入口换成了某个 API 请求的 callback 里。
Branch SDK
有很多第三方提供了 deep linking 和 deferred deep linking 的服务,比如 AppsFlyer 和 Branch。目前在 Glow 的 app 里这两个 SDK 都有用到。
其中 AppsFlyer 的优势在于他们跟很多公司有合作关系,比如 Facebook,所以用于追踪 Facebook 广告效果表现较好。另外 AppsFlyer 支持很多第三方服务的 server callback,可以方便集成很多第三方服务。缺点是 AppsFlyer 按 non-organic install 量收费。而且 AppsFlyer 的 SDK 和 API doc 写的不是很好,在 track 安装以后的后续 deep link 的时候感觉有很多 bug。
Branch 的优势在于免费,SDK 和 API doc 都写的比较好,而且有一些特殊的功能比如用户邀请及奖励之类的,适合做一些运营活动。另外 Branch 可以实现一个 link 根据平台自动跳转不同 Store,甚至可以在 desktop 上通过短信发送可以追踪的链接。缺点是 Branch 运营时间不久,服务稳定性有待验证,dashboard 的功能也还比较轻量。
总的来说 AppsFlyer 更适合 track 广告效果,Branch 更适合实现 feature。必须一提的是,因为这两个服务都是主要面向海外市场的,所以曾经都遇到过国内短暂抽风的现象,所以国内的 app 如果要用的话风险自担 :) 如果国内有类似的服务的话也欢迎留言补充。
Branch 的集成比较简单,参见官方文档。一个需要注意的是,自己实现的时候在 handle URL 或者 user activity 的时候可以直接处理 URL,但是用 Branch 的时候,第一级的 URL 是 Branch 的 URL,所以要通过 [[Branch getInstance] handleDeepLink:url]
和/或 [[Branch getInstance] continueUserActivity:userActivity]
交由 Branch 处理,然后在 init Branch 时传入的 block (closure) 中处理各类参数:
1
2
3
4
5
6
7
8
|
[branch initSessionWithLaunchOptions:launchOptions andRegisterDeepLinkHandler:^(NSDictionary *params, NSError *error) { if (!error) { // params are the deep linked params associated with the link that the user clicked -> was re-directed to this app // params will be empty if no data found // ... insert custom logic here ... NSLog(@ "params: %@" , params.description); } }]; |
iOS 中的 Deferred Deep Linking(延迟深度链接)的更多相关文章
- App引流增长技术:Deeplink(深度链接)技术
移动互联网时代,信息的分享传播无疑是 App 引流增长的关键,与其花费大量精力和成本找渠道.硬推广,不如从细节下手,用最快最简便的方法实现 Deeplink(深度链接)技术,打破信息孤岛.缩短分享路径 ...
- Deep Learning 19_深度学习UFLDL教程:Convolutional Neural Network_Exercise(斯坦福大学深度学习教程)
理论知识:Optimization: Stochastic Gradient Descent和Convolutional Neural Network CNN卷积神经网络推导和实现.Deep lear ...
- Cordoval在iOS中的运用整理
一:关于Cordoval理论知识 1:PhoneGap是手机平台上流行的一款中间件.它构建在各种手机平台所提供的WebView(浏览器内核)组件的基础之上,使用javascript语言对应用开发者提供 ...
- Deep Learning 13_深度学习UFLDL教程:Independent Component Analysis_Exercise(斯坦福大学深度学习教程)
前言 理论知识:UFLDL教程.Deep learning:三十三(ICA模型).Deep learning:三十九(ICA模型练习) 实验环境:win7, matlab2015b,16G内存,2T机 ...
- Deep Learning(深度学习)学习笔记整理
申明:本文非笔者原创,原文转载自:http://www.sigvc.org/bbs/thread-2187-1-3.html 4.2.初级(浅层)特征表示 既然像素级的特征表示方法没有作用,那怎样的表 ...
- iOS中几种定时器
在软件开发过程中,我们常常需要在某个时间后执行某个方法,或者是按照某个周期一直执行某个方法.在这个时候,我们就需要用到定时器. iOS中定时器NSTimer的使用 1.初始化 + (NSTimer ...
- ios中的几种多线程实现
iOS 支持多个层次的多线程编程,层次越高的抽象程度越高,使用起来也越方便,也是苹果最推荐使用的方法.下面根据抽象层次从低到高依次列出iOS所支持的多线程编程范式:1, Thread;2, Cocoa ...
- iOS 中对 HTTPS 证书链的验证
这篇文章是我一边学习证书验证一边记录的内容,稍微整理了下,共扯了三部分内容: HTTPS 简要原理: 数字证书的内容.生成及验证: iOS 上对证书链的验证. HTTPS 概要 HTTPS 是运行在 ...
- iOS中动画的简单使用
iOS中的动画右两大类1.UIView的视图动画2.Layer的动画 UIView的动画也是基于Layer的动画动画的代码格式都很固定 1.UIView动画 一般方式[UIView beginAnim ...
随机推荐
- Innodb_buffer_pool_read
> show status like 'Innodb_buffer_pool_read_%'; +---------------------------------------+-------- ...
- 基于宜搭的《T恤尺码收集》应用搭建
简介: 在阿里,T恤是程序员必不可少的元素.每逢公司或者BU(部门)的重大节庆日,比如双11 .年会.新BU成立仪式.大型活动等,都会给员工定制发放统一的T恤或者POLO衫服装.而我们每次发放T恤之前 ...
- dubbo入门学习(三)-----dubbo整合springboot
springboot节省了大量的精力去配置各种bean,因此通过一个简单的demo来整合springboot与dubbo 一.创建boot-user-service-provider 本篇博文基于上篇 ...
- DLINK 企业路由器内网部署web开启端口转发后还需要开启是否支持端口回流功能
跑后台使用的服务器,配置一般都很低,带宽只有2Mb 一些大型文件比如app的更新包使用这种服务器不可行 但是公司的网络是100Mb对等静态ip专线 所以能利用起来,每年将会省下8万块 说干就干,这个步 ...
- lumen使用CORS解决跨域问题
因为公司的业务是前后端分离,web前端和后端接口域名不同,所以存在跨域问题,开始使用的是jsonp解决,但是因为接口风格是rest的,还有delete.put等请求,jsonp就不够用了(涉及HTTP ...
- Redis学习03——存储字符串(String)
--------------------- 作者:愤怒的小明 来源:CSDN 原文:https://blog.csdn.net/qiwenmingshiwo/article/details/78118 ...
- workbench使用
1.你是指默认的mysql目录下data里面的'mysql'这个schema没有在workbench里面看到吧?点击菜单-Edit->Preferences里面的SQL Editor,然后把&q ...
- ubuntn16.04指令
基础知识: ubuntn中的/表示根目录,包括bin,mnt等文件夹 /home表示家目录,/home/user表示用户下的家目录,/root表示root目录 常用指令: 进入root : sudo ...
- Spring MVC中获取当前项目的路径
Spring MVC中获取当前项目的路径 在web.xml中加入以下内容 <!--获取项目路径--> <context-param> <param-name>web ...
- hdu 1269 (强联通分量Tarjan入门)
迷宫城堡 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...