iOS-使用URL Scheme实现app的进程间通信

iOS平台的进程间通信是一件很蛋疼的事,通过窝大量的资料搜索,最后发现好像比较普遍的实现方式则是使用URL Scheme(好吧好像别的方式我还没搞懂,准备看英文文献学习学习先哈哈哈哈哈)。

  URL Scheme就是将一个应用自身“绑定”到一个自定义的URL上,该Scheme可以用于在浏览器或别的应用中启动。PS:一个应用是可以自定义多个URL Scheme的。

注册自定义的URL Scheme

  首先我们在工程项目的Info.plist文件中添加以下信息。URL identifier一般推荐使用倒置域名方式以确保唯一性。而Scheme是一个数组,所以我们可以设置多个Scheme,之后我们会测试的。
Info.plist配置1

当然我们可以看一些生成的源码。
Info.plist配置2

URL Scheme启动应用初尝试

  通过上一步在Info.plist文件里配置了应用的URL Scheme之后,Build并Run一下应用。当应用被安装到模拟器上之后。我们则可以首先尝试用Safari来启动我们的应用了。

步骤:

  1. 运行以上应用。
  2. Shift+Command+H返回模拟器主界面。
  3. 然后打开Safari。
  4. 输入urlSchemeOne://,并点击确定。
  5. 就会弹出如下图,点击Open。然后我们的应用就打开了!

open url scheme

  有兴趣的童子试试urlSchemeTwo://也是可以打开应用的了,并且实际上URL Scheme是大小写不敏感的?= =因为我在safari中全输入的小谢也能打开。。。

在另一个app中启动自定义URL Scheme

  此时我们创建另一个工程,只需要实现一个功能,就是启动我们刚才设定了自定义URL Scheme的app。所以我们在里面设置了一个按钮。当我们点击该按钮时,则会触发打开URL Scheme的事件。首先我们来看一下代码的实现。

AppToOpenURLScheme

- (IBAction)openUrlScheme:(id)sender
{
    NSURL *url = [NSURL URLWithString:@"urlSchemeOne://"];

    //先判断一下是否可以启动该url
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        [[UIApplication sharedApplication] openURL:url];
    }
    else{
        NSLog(@"No such url.");
    }
}

解决问题-canOpenURL: failed for URL:XXX

  如果你是iOS 9.0以上的系统,当你运行上面的第二个工程后点击按钮,你应该会看到一串类似标题的异常输出,并且应用没有想期望的那样进行跳转。哈哈哈哈,这就是蛋疼的iOS 9.0以后的加入的新机制,我们应该还记得9.0同样限制了http请求,再次我们也需要像解除http请求一样在Info.plist中进行一些配置才行。
下面这个应该是会出现的异常信息。(如果没有你就忽略吧…)

2016-03-13 20:36:19.622 AppToOpenURLScheme[2637:1248171] -canOpenURL: failed for URL: "urlSchemeOne://" - error: "This app is not allowed to query for scheme urlSchemeOne"
2016-03-13 20:36:19.624 AppToOpenURLScheme[2637:1248171] No such url.

  通过我们查阅资料。我们发现传说有两种方式可以解除这个限制?= =。好吧说实话我两种都试了,但是有一个没弄出来。但是我还是介绍一下两种方法吧。

(1)第一种:支持http协议。额,这种方式我尝试了,没成功,如果有谁成功了记得教教我。。(后面我会贴上我查到的资料链接你们可以参考)这种方式就是在Info.plist中配置一下。

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

(2)第二种:配置scheme到LSApplicationQueriesSchemes。这种方式我是成功了的。下面介绍这种方法的配置方法。同样的是只需要配置Info.plist文件,而且是只需要在想要启动别人URL的app里设置就可以了!

PS:传说在模拟器下是会有问题,需要真机测试,我测试了真机上的确是正常的!!!

= =。比如说查到的资料,如果你是用了一些第三方SDK,一般都会要求在你自己的应用中配置别人的URL Scheme到LSApplicationQueriesSchemes中,这样你就能通过URL和别人的应用通信鸟~

AppToOpenURLScheme

<key>LSApplicationQueriesSchemes</key>
    <array>
        <string>urlSchemeOne</string>
        <string>urlSchemeTwo</string>
    </array>

进过以上配置之后,我们再次安装应用并点击按钮,应该就可以进行跳转了!

参考资料:适配ios9出现的问题:-canOpenURL: failed for URL

二、进程间数据传输

  我们之前讲了这么多最终的目标是什么!!!通信啊!!!传数据啊!!!= =。在这里就科普一个啥URL标准(虽然我也没看过),传说URL传递参数需要遵循RFC 1808标准。话不多说,我们开始吧!

  首先我们先实现接受数据的一端的方法。所以我们需要先返回先前创建了URL Scheme的应用中,在appDelegate.m文件中重写这两个方法。这些方法是在当有别的应用通过URL Scheme的方式启动时会调用的方法。

URLSchemaDemon

//实际过程中这个函数没被调用?- -。一脸懵逼。。。
-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
    NSLog(@"%@ %@", NSStringFromSelector(_cmd), url);
    return YES;
}

-(BOOL)application:(UIApplication *)application
           openURL:(NSURL *)url
 sourceApplication:(NSString *)sourceApplication
        annotation:(id)annotation
{
    NSLog(@"%@ %@", sourceApplication, url);
    if ([[url scheme] isEqualToString:@"urlSchemeOne"]) {
        NSLog(@"%@ %@", [url scheme], [url query]);
    }
    return YES;
}

  解决了接收端的问题后,我们来看发送数据的一方。我们只需要做一些小修改,将数据放在URL中进行传递就好了,当然实际中传递数据时是会对数据进行加密的。因为不排除有同名的URL Scheme的存在,这样可能会拦截传递的数据?然后导致数据泄露?

AppToOpenURLScheme

NSURL *url = [NSURL URLWithString:@"urlSchemeOne://?token=123456&id=10086"];

  然后我们重新编译运行应用,然后通过另一个应用打开该应用。则会看到如下输出信息。哈哈哈我们可以很清楚的看到通过URL传递的数据正确传递过来了~耶耶耶~对于sourceApplication参数我们一般可以用来限制只有特定的app启动我们应用时才做处理,因为我们无法限制被别的应用调用。

2016-03-13 21:25:13.915 URLSchemaDemon[2675:1257006] com.lysongzi.AppToOpenURLScheme urlSchemeOne://?token=123456&id=10086
2016-03-13 21:25:13.920 URLSchemaDemon[2675:1257006] urlSchemeOne token=123456&id=10086

  如果你是基于iOS 9.0进行开发的话,你会发现handleOpenURLopenURL:sourceApplication:annotation:两个方法在iOS 9.0以后官方已经不推荐使用了,转而推荐使用的则是这个方法``。

-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
{
    //可以通过option字典对象取出相应数据
    if ([[options objectForKey:UIApplicationOpenURLOptionsSourceApplicationKey] isEqualToString:@"com.lysongzi.AppToOpenURLScheme"]) {
        NSLog(@"%@ %@", [url scheme], [url query]);
    }
    return YES;
}

我们会发现实际上也是可以正确获取到传递过来的数据的鸟~~~

源代码

这里主要涉及了两个工程项目。

  1. 创建自定义的URL Scheme以及接受数据的工程:URLSchemaDemon
  2. 通过URL Scheme打开另一个应用,并传递数据:AppToOpenURLScheme

参考资料

  1. 苹果官方文档
  2. 适配ios9出现的问题:-canOpenURL: failed for URL
  3. ShareSDK for iOS
  4. 友盟分享 iOS9适配