iOS应用间跳转:从Open in my app聊到Deeplink

2024-05-08 15:08

本文主要是介绍iOS应用间跳转:从Open in my app聊到Deeplink,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

就我个人所知,iOS中存在3种方式可以打开(唤醒)其它手机App(除开系统级应用),分别是:

  • 第三方登录、分享、支付、导航等
  • Open in my app
  • Deeplink

三种方式原理一样,均是注册自定义URL Schemes,并处理URL请求。

4483590-fce645ae8b0ede32.jpeg
URL schemes.jpeg

第三方


  1. 使用第三方用户登录,如微信,QQ,微博登录,授权后返回用户名和密码
  2. 内容分享,跳转到分享App的对应页面,如分享给微信好友、分享给微信朋友圈、分享到微博
  3. 第三方支付,跳转到第三方支付App,如支付宝支付,微信支付
  4. 显示位置、地图导航,跳转到地图应用,如高德地图,百度地图等
  5. 应用程序推广,跳转到iTunes并显示指定App下载页,或使用Safari打开指定网页

1~4 第三方平台均提供了相应SDK,具体不再阐述,5实际只需要一个网址,调用[[UIApplication sharedApplication] openURL:url]方法即可。

在iOS9中,如果使用canOpenURL:方法,该方法所涉及到的URL Schemes必须在"Info.plist"中将它们列为白名单,否则不能使用。key叫做LSApplicationQueriesSchemes,键值内容是对应应用程序的URL Schemes

Open in my app


iOS有个不常使用的功能,叫Open in my app,即在我的app中打开,此功能允许文件在其他app中打开。

常见如邮件的附件,轻触苹果会默认使用QLPreviewController打开预览界面,而长按这时会弹出共享列表菜单,此菜单会列出所有添加了该类型文件的应用,它允许此文件在添加了对应Document Type支持的应用中打开,如下图所示:

4483590-8163dd03ba2bf443.jpeg
Open in my app

实现步骤

1. 修改Info.plist文件

1)在plist文件中添加URLTypes字段,指定程序的对外接口:

4483590-ce6e3dda85936e04.jpeg
Info.plist

由于我之前已经集成了社会化分享,这一步就直接跳过。

2)添加一个Documents Type字段,该字段指定与程序关联的文件类型,详情参考System-Declared Uniform Type Identifiers

CFBundleTypeExtensions指定文件类型,例如pdfdocxlsppttxt等。
CFBundleTypeIconFiles指定用UIActionSheet向用户提示打开应用时显示的图标。
DocumentTypeName可以自定,对应文件类型名。
Document Content Type UTIs指定官方指定的文件类型,UTIs 即 Uniform Type Identifiers。

  • PDF文件

    4483590-6f954b7bf5293a04.jpeg
    .pdf的配置

  • Doc文件

    4483590-203a43f173ee0990.jpeg
    .doc(s)的配置

  • Excel文件

    4483590-bf9ad1e0e04990d3.jpeg
    .xls(x)的配置

  • PPT文件

    4483590-73bbe0fc98dea16d.png
    .ppt的配置

注意:预支持PPT需要 额外配置增加public.data字段,被这个坑了好久!

  • TXT(或RTF)
    4483590-1aa6ddb6146fc8dd.jpeg
    .txt的配置
2. 在 AppDelegate.m 中添加外部调用代码
#import <QuickLook/QuickLook.h>@interface AppDelegate ()  <QLPreviewControllerDataSource>
@property (nonatomic,strong) NSURL *openUrl; 
@end

注意:iOS 9之后请使用这个方法:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options;

-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {if (url && [url isFileURL]) {self.openUrl=url;       QLPreviewController* previewController = [[QLPreviewController alloc] init];[self.window.rootViewController presentViewController:previewController animated:YES completion:nil];    dispatch_async(dispatch_get_global_queue(0, 0), ^{      NSData *data = [NSData dataWithContentsOfURL:url];dispatch_async(dispatch_get_main_queue(), ^{NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);NSString *documentsPath=[[paths objectAtIndex:0] stringByAppendingPathComponent:@"downloadFile"];      BOOL dirHas;if (![[NSFileManager defaultManager] fileExistsAtPath:dirPath isDirectory:&dirHas] ) {[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:NO attributes:nil error:nil];}  NSString *filePath = [documentsPath stringByAppendingPathComponent:[[url relativePath] lastPathComponent]];//Add the file name[data writeToFile:filePath atomically:YES];//写入成功后再读取刷新数据,避免跳转界面时等待太久previewController.dataSource = self;[previewController reloadData];});});return YES;}return NO;
} //使用QLPreviewController预览
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller {return 1;
}- (id <QLPreviewItem>)previewController: (QLPreviewController*)controller previewItemAtIndex:(NSInteger)index {
//    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
//    NSString *docDir = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Inbox"];NSString *filePath=[getDocumentPath() stringByAppendingPathComponent:[[self.openUrl relativePath] lastPathComponent]];if ([[filePath pathExtension] isEqualToString:@"txt"]) {//处理txt格式内容显示有乱码的情况NSData *fileData = [NSData dataWithContentsOfFile:filePath];//判断是UNICODE编码NSString *isUNICODE = [[NSString alloc] initWithData:fileData encoding:NSUTF8StringEncoding];//还是ANSI编码(-2147483623,-2147482591,-2147482062,-2147481296)encoding 任选一个就可以了NSString *isANSI = [[NSString alloc] initWithData:fileDataencoding:-2147483623];if (isUNICODE) {NSString *retStr = [[NSString alloc]initWithCString:[isUNICODE UTF8String] encoding:NSUTF8StringEncoding];NSData *data = [retStr dataUsingEncoding:NSUTF16StringEncoding];[data writeToFile:filePath atomically:YES];} else if (isANSI) {NSData *data = [isANSI dataUsingEncoding:NSUTF16StringEncoding];[data writeToFile:filePath atomically:YES];}}NSURL *fileUrl = [NSURL fileURLWithPath:filePath];return fileUrl;
}

至于为什么要下载?
我经过测试发现文件都是存在沙盒Documents/Inbox/目录下的,获取到的文件路径例如:file:///private/var/mobile/Containers/Data/Application/A9D2A924-A1F8-4915-B31D-57127ED987BE/Documents/Inbox/工作日报_0113.xlsx,然而却怎么也打不开,尝试拼接取文件路径也不成功,于是重新下载了一份放到我指定的目录下,也可以直接使用copyItemAtPath: toPath: error:方法拷贝文件到指定的路径下。

更多支持文件类型(如MP3,AIFF,WAV, etc.)

stack overflow给出了各个类型所需要添加的plist字段:
http://stackoverflow.com/questions/9266079/why-is-my-ios-app-not-showing-up-in-other-apps-open-in-dialog

Deeplink(深度链接)


Deeplink,简单来说就是给定一个链接,即可跳转到已安装应用中的某一个页面的技术。

通过Deep Link,App可以通过搜索引擎进行导流。可以通过 SEO 增加访问量从而提高 app 下载量和开启率,可以实现Web与App间切换保留上下文信息。

最初听到Deeplink是在魔窗的宣讲会,那时一脸懵,回来仔细了解了下,相比推送跳转的方式(现在很多用户都是直接关闭推送的),通过分享邀请和短信唤醒,对运营拉新、拉活、留存、转化,提供了更多帮助。

技术要求:
  • APP要想被其他APP直接打开,自身得支持,让自己具备被人打开的能力。(URL Schemes)
  • APP要想打开其他的APP,自身也得支持。(判断设备是否安装、各种跳转的处理)

iOS 9以上的用户,可以通过Universal Links通用链接无缝的重定向到一个App应用,而不需要通过Safari打开跳转。
如果用户没有安装这个app,则会在Safari中打开这个链接指向的网页。
唤醒方法为:- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler;

栗子?(关于Universal Links的具体配置建议查看官方文档 苹果有个网址可以测试apple-app-site-association是否有效测试地址)

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler{if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {NSURL *webUrl = userActivity.webpageURL;if ([webUrl.host isEqualToString:@"com.example.ios"]) {//打开对应页面
//            NSString *paramStr = [[webUrl query] substringFromIndex:[[webUrl query] rangeOfString:@"?"].location+1];NSDictionary *paramDic = [[webUrl absoluteString] getURLParameters];NSLog(@"paramDic: %@",paramDic);//跳转本地ViewController} else {//不能识别,safari打开[[UIApplication sharedApplication] openURL:webUrl];}}return YES;
}//  NSString+UrlEncoding.h
/***  截取URL中的参数**  @return NSDictionary parameters*/
- (NSDictionary *)getURLParameters {// 查找参数
NSRange range = [self rangeOfString:@"?"];
if (range.location == NSNotFound) {return nil;
}NSMutableDictionary *params = [NSMutableDictionary dictionary];// 截取参数
NSString *parametersString = [self substringFromIndex:range.location + 1];// 判断参数是单个参数还是多个参数
if ([parametersString containsString:@"&"]) {// 多个参数,分割参数NSArray *urlComponents = [parametersString componentsSeparatedByString:@"&"];for (NSString *keyValuePair in urlComponents) {// 生成Key/ValueNSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];NSString *key = [pairComponents.firstObject stringByRemovingPercentEncoding];NSString *value = [pairComponents.lastObject stringByRemovingPercentEncoding];// Key不能为nilif (key == nil || value == nil) {continue;}id existValue = [params valueForKey:key];if (existValue != nil) {// 已存在的值,生成数组if ([existValue isKindOfClass:[NSArray class]]) {// 已存在的值生成数组NSMutableArray *items = [NSMutableArray arrayWithArray:existValue];[items addObject:value];[params setValue:items forKey:key];} else {// 非数组[params setValue:@[existValue, value] forKey:key];}} else {// 设置值[params setValue:value forKey:key];}}
} else {// 单个参数// 生成Key/ValueNSArray *pairComponents = [parametersString componentsSeparatedByString:@"="];// 只有一个参数,没有值if (pairComponents.count == 1) {return nil;}// 分隔值NSString *key = [pairComponents.firstObject stringByRemovingPercentEncoding];NSString *value = [pairComponents.lastObject stringByRemovingPercentEncoding];// Key不能为nilif (key == nil || value == nil) {return nil;}// 设置值[params setValue:value forKey:key];
}return [NSDictionary dictionaryWithDictionary:params];
}

测试方法:在短信或备忘录中输入相应域名,若能跳转到相应App即植入成功。直接在Safari中输入链接是无效的,必须从一处跳入才可以(比如上一级网页)。

Deferred Deeplink(延迟深度链接)

Deeplink只针对手机中已经安装过App的用户才有用。而升级版本的Deferred Deeplink却可以解决这个问题:

Deferred Deeplink 可以先判断用户是否已经安装了App应用,如果没有则先引导至App应用商店中下载App, 在用户安装App后跳转到指定App页面 Deeplink 中。

Deferred Deeplink的应用场景:
  • 追踪广告效果
  • 追踪用户推荐/邀请链接
  • 在 app 内保持网页浏览的上下文,如登录信息,购物车等

App分享邀请好友,好友通过链接(只有通过此链接跳转到App Store下载App才算有效)安装App之后双方获得奖励,省去了过去注册输入邀请码这一步。

这篇关于iOS应用间跳转:从Open in my app聊到Deeplink的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/970666

相关文章

Python中re模块结合正则表达式的实际应用案例

《Python中re模块结合正则表达式的实际应用案例》Python中的re模块是用于处理正则表达式的强大工具,正则表达式是一种用来匹配字符串的模式,它可以在文本中搜索和匹配特定的字符串模式,这篇文章主... 目录前言re模块常用函数一、查看文本中是否包含 A 或 B 字符串二、替换多个关键词为统一格式三、提

Java MQTT实战应用

《JavaMQTT实战应用》本文详解MQTT协议,涵盖其发布/订阅机制、低功耗高效特性、三种服务质量等级(QoS0/1/2),以及客户端、代理、主题的核心概念,最后提供Linux部署教程、Sprin... 目录一、MQTT协议二、MQTT优点三、三种服务质量等级四、客户端、代理、主题1. 客户端(Clien

CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比

《CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比》CSS中的position属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布... css 中的 position 属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布局和层叠关

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

Python使用Tkinter打造一个完整的桌面应用

《Python使用Tkinter打造一个完整的桌面应用》在Python生态中,Tkinter就像一把瑞士军刀,它没有花哨的特效,却能快速搭建出实用的图形界面,作为Python自带的标准库,无需安装即可... 目录一、界面搭建:像搭积木一样组合控件二、菜单系统:给应用装上“控制中枢”三、事件驱动:让界面“活”

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

Python Flask 库及应用场景

《PythonFlask库及应用场景》Flask是Python生态中​轻量级且高度灵活的Web开发框架,基于WerkzeugWSGI工具库和Jinja2模板引擎构建,下面给大家介绍PythonFl... 目录一、Flask 库简介二、核心组件与架构三、常用函数与核心操作 ​1. 基础应用搭建​2. 路由与参

Spring Boot中的YML配置列表及应用小结

《SpringBoot中的YML配置列表及应用小结》在SpringBoot中使用YAML进行列表的配置不仅简洁明了,还能提高代码的可读性和可维护性,:本文主要介绍SpringBoot中的YML配... 目录YAML列表的基础语法在Spring Boot中的应用从YAML读取列表列表中的复杂对象其他注意事项总

电脑系统Hosts文件原理和应用分享

《电脑系统Hosts文件原理和应用分享》Hosts是一个没有扩展名的系统文件,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应... Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应

CSS 样式表的四种应用方式及css注释的应用小结

《CSS样式表的四种应用方式及css注释的应用小结》:本文主要介绍了CSS样式表的四种应用方式及css注释的应用小结,本文通过实例代码给大家介绍的非常详细,详细内容请阅读本文,希望能对你有所帮助... 一、外部 css(推荐方式)定义:将 CSS 代码保存为独立的 .css 文件,通过 <link> 标签