我对“是否安装某App”问题的第一反应是,调用系统UIApplication对象的实例方法canOpenURL:来判断,一般在自己得出习惯性的思维答案后,就觉得满足,所以今天得一反常态,重新认识一下这个功能需求背后到底隐藏了哪些不曾为我所知的㊙️秘密㊙️在里面
一、为啥要判断系统是否安装了这个App?(为什么)
总的来说,就是为了提升用户体验
,摒除额外操作造成的时间浪费
。
结合目前自己做过的项目我觉得这个需求点的应用场景有如下几个:
- 公司内的 App 产品数多于2个,并且业务间有联系,各自的App信息界面有共享功能,如:
A 中有商家店铺的
基本信息
,但不具备修改功能
B 中则有商家的店铺详细信息
,且具有可修改功能
那么使用A app 的商家,如需更改其店铺信息则需要切换到 B app 上执行相关动作。
- 公司为导流业务,维护一些马甲App,需从马甲 App 跳到 或 推荐安装自己的主运营 App 上。
- 调用系统App,如:短息,Email,通讯录,iTunes...等相关情况。
二、用到了哪些API?(是什么)
就从我的第一反应源(canOpenURL:)入手吧。
- (BOOL)canOpenURL:(NSURL *)url NS_AVAILABLE_IOS(3_0);`
没有更详细信息,那去https://developer.apple.com查查canopenurl:,看有无新发现。
映入眼帘的描述啊:
Returns a Boolean value indicating whether or not the URL’s scheme can be handled by some app installed on the device.
大概意思是:
判断该方法传入的URL’s scheme参数是否能够被已安装在此设备上的一些App处理。
那 URL'scheme
是啥呢?
同样我们顺便看看 Discussion 部分吧,因为一般注意事项都会罗列这个 section,不看就亏了不是吗:
- URL Scheme是类似 http://,ftp://,afp:// 这样的东西,通常是用传输协议作为URL Scheme。
- 不过事实上,你可以在iOS注册任何类型的URL Scheme。当用户通过系统 API 访问你的自定义URL Scheme的链接的时候,操作系统就会打开你的程序,响应这个请求。
注意点大概有:
- 当该方法返的结果是 YES 的话,iOS 会保证之后通过调用相同 URL 参数的
openURL:
方法成功启动的 App 一定能够处理该 URL 参数 -
canOpenURL:
方法返回的结果不代表URL 的正确性
或者URL 描述的资源存在性
Important:
- 如果 App 的 version >= iOS 9.0, 则必须先声明作为参数传入canOpenURL:方法的 URL schemes;
- 通过在 app 的 Info.plist 文件上添加LSApplicationQueriesSchemes key对 URL 声明 ;
- 如果未执行先声明动作,而调用canOpenURL:时则会一直返回 FALSE;无论是否有已安装且可处理该 URL 的 App 在本设备上
- 如果你的 app linked 的版本低于 iOS9.0,却运行在 iOS9.0或以上的话,那你只能正确调用canOpenURL:方法50次,超过次数之后的调用会一直返回 NO; 除非用户重新安装或升级App,则会重置该限制,
-
openURL:
与canOpenURL:
不同的是,openURL:
方法不受LSApplicationQueriesSchemes 要求限制,即不管你有无先声明该 URL schemes 在 Info.plist 的 LSApplicationQueriesSchemes内,只要有 app 能够打开该 URL 即有效;
如下是openURL:
(iOS 2 ~ 10可用)方法声明:
- (BOOL)openURL:(NSURL*)url NS_DEPRECATED_IOS(2_0, 10_0, "Please use openURL:options:completionHandler: instead")NS_EXTENSION_UNAVAILABLE_IOS("");
如下是 openURL:options:completionHandler:
(iOS 10+可用)方法声明
// Options are specified in the section below for openURL options. An empty options dictionary will result in the same
// behavior as the older openURL call, aside from the fact that this is asynchronous and calls the completion handler rather
// than returning a result.
// The completion handler is called on the main queue.
- (void)openURL:(NSURL*)url options:(NSDictionary *)options completionHandler:(void (^ __nullable)(BOOL success))completion NS_AVAILABLE_IOS(10_0) NS_EXTENSION_UNAVAILABLE_IOS("");
既然出现自己不熟悉的LSApplicationQueriesSchemes key,今天就要一窥到底!
说的是:
- 这个 key 用于 iOS 9.0 and later;
- 该key对应的类型为数组,用于存放通过 canOpenURL:方法( UIApplication类的实例方法)判断的能否跳转的 URL Schemes;(此描述不太恰当,详细情况文档)
同样还有一个我们之前就一直在用的与跳转白名单相关概念 CFBundleURLTypes key
截个图让大家与我一起复习一下吧:
三、怎么去执行以上步骤(配置 LSApplicationQueriesSchemes 与 CFBundleURLTypes)?(怎么做)
1、配置LSApplicationQueriesSchemes 。
声明:
“1、配置LSApplicationQueriesSchemes (添加Scheme白名单)。”摘自《mob的适配 ios - 9 必读》
问题描述:在iOS 9下涉及到平台客户端跳转,系统会自动到项目info.plist下检测是否设置平台Scheme。对于需要配置的平台,如果没有配置,就无法正常跳转平台客户端。因此要支持客户端的分享和授权等,需要配置Scheme名单。
具体方法:
1)、在项目的info.plist中添加一LSApplicationQueriesSchemes,类型为Array。
2)、然后给它添加一个需要支持的项目,类型为字符串类型;
必看注意
1.在iOS9中,如果没有添加上述白名单,系统会打印类似如下提示:.-canOpenURL: failed for URL: “sinaweibohdsso://xxx” – error: “This app is not allowed to query for scheme sinaweibohdsso”(如下图)如没有添加相关白名单,有可能导致分享失败,例如不会跳转微信,不会跳转****QQ****等。
2.添加完上述所需的名单,系统依然会打印类似信息:.-canOpenURL: failed for URL: “sinaweibohdsso://xxx” – error: “null”这是系统打印的信息,目前是无法阻止其打印,即无法消除的
如果没有设置白名单的话,系统的打印信息如图所示:
添加完后,系统是依然会打印的,不过error会变成null:
2、配置CFBundleURLTypes 。(估计这个你也做过好多遍了吧☺️)
- 设置URL Schemes (在需要跳转的APP中设置自己的Scheme)
- 接上配置LSApplicationQueriesSchemes的配置图(因为如果在 iOS 9+ 环境下调用 canOpenURL:方法需要同时配置LSApplicationQueriesSchemes )
四、应用间跳转
-
在 One App 中注册设置需要跳转的URL Scheme
-
可以在Safari 中测试该 URL Scheme 是否能被跳转
注意:APP URL格式为: URL Scheme://urlidentifier,直接调用URL Scheme也可打开程序, url identifier是可选的。
-
在Two App 中的 info Plist 中添加白名单 URL Scheme
在 Two App 中添加跳转到One App代码
- (IBAction)jumpToOneApp:(id)sender {
NSString *urlscheme = @"oneapp://";
[self checkIfInstalledAppWithUrlSchemes:urlscheme];
}
#pragma mark - 判断是否安装了APP 如安装则跳转到对应 App
- (void)checkIfInstalledAppWithUrlSchemes:(NSString *)urlScheme {
NSURL *URL = [NSURL URLWithString:urlScheme];
UIApplication *application = [UIApplication sharedApplication];
/*
// 方式一 :
// 判断是否安装了APP
if ([application canOpenURL:URL]) {
NSLog(@"已经安装,并且可以打开");
if ([application respondsToSelector:@selector(openURL:options:completionHandler:)]) {
// iOS10及以上判断方式
[application openURL:URL options:@{} completionHandler:^(BOOL success) {
NSLog(@"iOS10及以上Open %@: 是否成功%d",urlScheme,success);
if (!success) {
// 没有成功
NSLog(@"iOS10 进入app失败");
}
}];
} else {
BOOL success = [application openURL:URL];
NSLog(@"Open %@: %d",urlScheme,success);
if (!success) {
// 没有成功
NSLog(@"进入app失败");
}
}
} else {
NSLog(@"不能打开");
}
*/
// 方式二:
// 直接进入,不成功就弹出提示即可, 建议使用这种方式
if ([application respondsToSelector:@selector(openURL:options:completionHandler:)]) {
// iOS10及以上判断方式
[application openURL:URL options:@{} completionHandler:^(BOOL success) {
NSLog(@"iOS10及以上Open %@: 是否成功%d",urlScheme,success);
if (!success) {
// 没有成功
NSLog(@"iOS10 进入app失败");
}
}];
} else {
BOOL success = [application openURL:URL];
NSLog(@"Open %@: %d",urlScheme,success);
if (!success) {
// 没有成功
NSLog(@"进入app失败");
}
}
}
运行效果:
使用 URL Scheme 注意点
- URL Identifier 的唯一性
- iOS9 之后的白名单 (LSApplicationQueriesSchemes)
- iOS 10 之后的 OpenURL 被弃用情况判断
跳转测试 Demo
五、总结
- 个人觉得要实现一个 App间 跳转功能确实不难,而且网络上已经有很多先关的 demo,同时如果有使用过一些较有名的分享 SDK (Mob share 、友盟分享等) 的话都会碰到这些配置操作的详细相关描述,只要跟着一步一步走就能实现,而难点就在于,知其然而不知其所以然吧,特别是一些概念性的问题,有些专业名词确实理解起来有点难,还是那一句话,多看官方文档描述,这样子就能以不变应万变吧。(由于本人英语理解力有限,怕误人子弟,所以都贴上了图及连接,以上个人文字仅从个人理解所写)
- 原本只想自己总结一下是“判断否安装xApp”这个问题,虽知道,判断之后就是跳转啊之类的,那就索性随便写一下啰,希望能够给各位阅读者有一点点的帮助
六、参考文章
《判断iOS设备上是否安装某个应用以及应用跳转》
iOS中的URL Scheme
iOS 关于微信检测SDK应用的原理浅析
Mob 的 适配 iOS 9+系统
【从0开发搭建App】IOS9中canOpenURL调用失败分析
在Mac和iOS中注册自定义的URL Scheme
Demo3_iOS9URLScheme适配_引入白名单概念
About Apple URL Schemes
iOS Universal Links & URL Scheme