URL Scheme / 系统是否安装某App / 应用间跳转 整理

我对“是否安装某App”问题的第一反应是,调用系统UIApplication对象的实例方法canOpenURL:来判断,一般在自己得出习惯性的思维答案后,就觉得满足,所以今天得一反常态,重新认识一下这个功能需求背后到底隐藏了哪些不曾为我所知的㊙️秘密㊙️在里面

我只想知道真相

一、为啥要判断系统是否安装了这个App?(为什么)

总的来说,就是为了提升用户体验,摒除额外操作造成的时间浪费

结合目前自己做过的项目我觉得这个需求点的应用场景有如下几个:

  1. 公司内的 App 产品数多于2个,并且业务间有联系,各自的App信息界面有共享功能,如:

A 中有商家店铺的基本信息,但不具备修改功能
B 中则有商家的店铺详细信息,且具有可修改功能

那么使用A app 的商家,如需更改其店铺信息则需要切换到 B app 上执行相关动作。

  1. 公司为导流业务,维护一些马甲App,需从马甲 App 跳到推荐安装自己的主运营 App 上。
  2. 调用系统App,如:短息,Email,通讯录,iTunes...等相关情况。

二、用到了哪些API?(是什么)

就从我的第一反应源(canOpenURL:)入手吧。

先看看系统对canOpenURL:的描述:
摘自 UIApplication.h sdk(Xcode 8.3)
- (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 是啥呢?

  1. URL Scheme是类似 http://ftp://afp:// 这样的东西,通常是用传输协议作为URL Scheme。
  2. 不过事实上,你可以在iOS注册任何类型的URL Scheme。当用户通过系统 API 访问你的自定义URL Scheme的链接的时候,操作系统就会打开你的程序,响应这个请求。

同样我们顺便看看 Discussion 部分吧,因为一般注意事项都会罗列这个 section,不看就亏了不是吗:
openURL: Discussion part

注意点大概有:

  1. 当该方法返的结果是 YES 的话,iOS 会保证之后通过调用相同 URL 参数的openURL:方法成功启动的 App 一定能够处理该 URL 参数
  2. canOpenURL:方法返回的结果不代表 URL 的正确性 或者 URL 描述的资源存在性

Important:

  1. 如果 App 的 version >= iOS 9.0, 则必须先声明作为参数传入canOpenURL:方法的 URL schemes;
  2. 通过在 app 的 Info.plist 文件上添加LSApplicationQueriesSchemes key对 URL 声明 ;
  3. 如果未执行先声明动作,而调用canOpenURL:时则会一直返回 FALSE;无论是否有已安装且可处理该 URL 的 App 在本设备上
  1. 如果你的 app linked 的版本低于 iOS9.0,却运行在 iOS9.0或以上的话,那你只能正确调用canOpenURL:方法50次,超过次数之后的调用会一直返回 NO; 除非用户重新安装或升级App,则会重置该限制
  2. 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<NSString *, id> *)options completionHandler:(void (^ __nullable)(BOOL success))completion NS_AVAILABLE_IOS(10_0) NS_EXTENSION_UNAVAILABLE_IOS("");

既然出现自己不熟悉的LSApplicationQueriesSchemes key,今天就要一窥到底!

LSApplicationQueriesSchemes

说的是:

  1. 这个 key 用于 iOS 9.0 and later;
  2. 该key对应的类型为数组,用于存放通过 canOpenURL:方法( UIApplication类的实例方法)判断的能否跳转的 URL Schemes;(此描述不太恰当,详细情况文档)
    同样还有一个我们之前就一直在用的与跳转白名单相关概念 CFBundleURLTypes key
    截个图让大家与我一起复习一下吧:
    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:
没设置白名单 系统 error 为 null

2、配置CFBundleURLTypes 。(估计这个你也做过好多遍了吧☺️)

  1. 设置URL Schemes (在需要跳转的APP中设置自己的Scheme)
    图片引《自判断iOS设备上是否安装某个应用以及应用跳转》

    图片引《自判断iOS设备上是否安装某个应用以及应用跳转》
  2. 接上配置LSApplicationQueriesSchemes的配置图(因为如果在 iOS 9+ 环境下调用 canOpenURL:方法需要同时配置LSApplicationQueriesSchemes )
    图片引《自判断iOS设备上是否安装某个应用以及应用跳转》

四、应用间跳转

  1. 在 One App 中注册设置需要跳转的URL Scheme


    设置URL Types
  2. 可以在Safari 中测试该 URL Scheme 是否能被跳转


    Safari中测试URLScheme.gif

注意:APP URL格式为: URL Scheme://urlidentifier,直接调用URL Scheme也可打开程序, url identifier是可选的。

  1. 在Two App 中的 info Plist 中添加白名单 URL Scheme


    Two App 中的 info Plist 中添加白名单 Url
  2. 在 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失败");       
        }
    }
}

运行效果:


JumpToOne.gif

使用 URL Scheme 注意点

  • URL Identifier 的唯一性
  • iOS9 之后的白名单 (LSApplicationQueriesSchemes)
  • iOS 10 之后的 OpenURL 被弃用情况判断

跳转测试 Demo

五、总结

  1. 个人觉得要实现一个 App间 跳转功能确实不难,而且网络上已经有很多先关的 demo,同时如果有使用过一些较有名的分享 SDK (Mob share 、友盟分享等) 的话都会碰到这些配置操作的详细相关描述,只要跟着一步一步走就能实现,而难点就在于,知其然而不知其所以然吧,特别是一些概念性的问题,有些专业名词确实理解起来有点难,还是那一句话,多看官方文档描述,这样子就能以不变应万变吧。(由于本人英语理解力有限,怕误人子弟,所以都贴上了图及连接,以上个人文字仅从个人理解所写)
  2. 原本只想自己总结一下是“判断否安装xApp”这个问题,虽知道,判断之后就是跳转啊之类的,那就索性随便写一下啰,希望能够给各位阅读者有一点点的帮助

六、参考文章

推荐阅读更多精彩内容