美文网首页iOS学习专题iOS技术资料iOS知识收集
【iOS开发】打开另一个APP(URL Scheme与openU

【iOS开发】打开另一个APP(URL Scheme与openU

作者: 谦言忘语 | 来源:发表于2016-07-16 22:53 被阅读57195次

    目标

    平常我们做iOS开发,会经常遇到打开其他的APP的功能。本篇文章讲的就是打开别人的APP的一些知识。我们的目标是:

    • 打开别人的APP
    • 让别人打开我们的APP
    • 版本大于等于iOS9的适配问题
    • 使用URL Schemes传递数据

    准备工作

    • 建立一个名为OpenApp的工作空间,用来存放我们的两个工程
      建立这个工作空间主要是为了让我们后面建立的两个工程能在一个Xcode页面上管理,方便讲解和管理。挺简单的,不清楚的可以看我之前的文章【iOS开发】在一个Xcode页面建立多个工程
    建立一个workspace来存放我们的工程
    • 建立一个名为MyApp的iOS工程。这个MyApp是“我的app”,用来打开另一个APP的。
      添加工程到我们刚才创建的OpenApp.xcworkspace。并且在工程的Main.storyboard添加一个button,待会儿我们会用来写方法。


      MyApp工程
    • 建立一个名为WXApp的iOS工程。这个工程是我们模拟的“微信APP”,是被人打开的那个APP。
      为了区分两个应用,我们在Main.storyboard上加一个label,“我是微信App”。


      在workspace建立WXApp工程
    WXApp

    好了,准备工作就这么简单。

    打开别人的APP与让别人打开我们的APP

    想要打开别人的APP或者让别人打开我们的APP,那就需要通过URL Schemes了。

    什么是URL Schemes?

    URL Schemes是苹果给出的用来跳转到系统应用或者跳转到别人的应用的一种机制。同时还可以在应用之间传数据。

    通过对比网页链接来理解 iOS 上的 URL Schemes,应该就容易多了。
    URL Schemes 有两个单词:

    • URL,我们都很清楚,http://www.apple.com
      就是个 URL,我们也叫它链接或网址;
    • Schemes,表示的是一个 URL 中的一个位置——最初始的位置,即 ://
      之前的那段字符。比如 http://www.apple.com
      这个网址的 Schemes是 http
      根据我们上面对 URL Schemes 的使用,我们可以很轻易地理解,在以本地应用为主的 iOS 上,我们可以像定位一个网页一样,用一种特殊的 URL 来定位一个应用甚至应用里某个具体的功能。而定位这个应用的,就应该是这个应用的 URL 的 Schemes 部分,也就是开头儿那部分。

    在WXApp上设置一个URL Schemes

    为了能让别的App(包括我们刚才创建的MyApp)能够打开WXApp,我们需要为WXApp添加一个URL Schemes。
    步骤:选中WXApp工程->Info->URL Types->点击“+”->在URL Schemes栏填上 weixin

    添加一个URL Schemes

    备注:一个应用是可以有多个URL Schemes的。你可以再次点击“+”来添加一个URL Schemes

    我们看看info.plist文件里面是怎样的。

    info.plist文件里面的URL Schemes

    然后我们run一下WXApp。(注意一下你run的target是哪个)

    在模拟器run一下WXApp

    这样,WXApp就向系统“注册”了一个URL Schemes,其他的应用可以通过openurl:方法来打开WXApp了。

    MyApp打开WXApp

    现在我们在MyApp里面打开WXApp。方法非常简单。
    在ViewController里面添加一个方法

    - (IBAction)openWXApp:(UIButton *)sender {
        [self demo1];
    }
    - (void)demo1 {
        //创建一个url,这个url就是WXApp的url,记得加上://
        NSURL *url = [NSURL URLWithString:@"weixin://"];
        
        //打开url
        [[UIApplication sharedApplication] openURL:url];
    }
    

    然后run一下MyApp

    run MyApp

    运行了之后点击“打开微信”button,会弹出“MyApp”想要打开“WXApp”提示框,点确认之后就可以跳转到WXApp了。

    点击“打开微信”button 打开了WXApp

    iOS9之后,在一个应用跳转到了另一个应用之后,左上角会有个返回到上一个应用的按钮。这样,我们在MyApp里面点击“打开微信”按钮,跳转到WXApp之后,再点击“Back to MyApp”,又回到MyApp了。闲着无聊就可以反复点击这两个按钮来两个应用间跳转了,哈哈。

    值得一说的是,这个URL Schemes并不是唯一的。也就是说,多个应用之间设置的URL Schemes是可以相同的。
    那么问题来了,假如两个应用的URL Schemes相同的话,使用openURL:方法会打开哪个应用呢?
    楼主亲自用手机试了一下。
    步骤是:

    • 将MyApp安装到手机上,点击“打开微信”button,微信打开了。
    • 然后将WXApp也安装到手机上。再次点击MyApp的“打开微信”button,结果打开的是WXApp。
      结论:如果两个应用有URL Schemes是相同的,后安装的应用的URL Schemes会把早安装的应用的URL Schems覆盖掉。

    20180528编辑:后安装的应用的URL Schemes会把早安装的应用的URL Schems覆盖掉,这个结论是不合理的。具体的话也没测试出结果。就当留个坑吧。

    在safari打开WXApp

    没错,注册了URL Schemes的应用,用safari浏览器也是可以打开的。我就经常用这个来验证应用是否设置了我想要的URL Schemes
    在safari打开WXApp,直接在safari的地址栏输入weixin://,enter就可以打开了

    用safari打开WXApp 用safari打开WXApp

    版本大于等于iOS9的适配问题

    • 配置URL Schemes白名单
      其实在打开WXApp的时候,正常情况下,我们应该是先用canOpenURL:方法先判断能否打开这个url,然后再用openURL方法打开该URL的。这样可以带来更好的用户体验。因为用户不一定安装了WXApp。假如用户没有安装的话点击“打开微信”button是没有任何反应的。这时候我们应该先判断是否能够打开这个url(也就是判断用户有没有安装WXApp),没有安装的话就给个温馨提示,比如:“U四不四洒,没安装WXApp,怎么打开啊!”。
      更重要的是,假如点击之后没效果,送审很有可能被苹果拒绝哦。
    - (IBAction)openWXApp:(UIButton *)sender {
    //    [self demo1];
        [self demo2];
    }
    //先判断再打开WXApp
    - (void)demo2 {
        //创建一个url,这个url就是WXApp的url,记得加上://
        NSURL *url = [NSURL URLWithString:@"weixin://"];
    
        //先判断是否能打开该url
        if ([[UIApplication sharedApplication] canOpenURL:url]) {
            //打开url
            [[UIApplication sharedApplication] openURL:url];
        }else {
            //给个提示或者做点别的事情
            NSLog(@"U四不四洒,没安装WXApp,怎么打开啊!");
    }
    

    但是我们发现用了canOpenURL:方法之后,并没有如我们想像中打开了WXApp。一看,Xcode控制台提示:

    Xcode控制台错误提示

    为什么会这样呢?
    因为iOS9的时候苹果加强了权限,只有在info.plist文件中加入了URL Schemes白名单才能使用canOpenURL:方法来判断是否能打开该url。该白名单的上限是50个。也就是说,你最多只能使用canOpenURL:方法判断50个URL Schemes。当然,平常我们都用不了那么多,就算是集成分享功能,50个肯定够了。

    备注:只是对canOpenURL:方法有限制,openURL:方法是没有限制的。

    言归正传,我们需要在MyApp的info.plist里面将weixin设置为白名单。
    步骤:点击info.plist->右键->Open As->Source Code->添加下面的代码

        <key>LSApplicationQueriesSchemes</key>
        <array>
            <string>weixin</string>
        </array>
    

    这样就可以了。

    使用URL Schems传递数据

    URL Schemes除了可以用来打开APP之外,还可以用来在两个App之间传递少量的数据。
    在百度上搜索“ios”,会生成一个url,下面来以这个url来大概介绍url的组成。

    url为:https://www.baidu.com/s?ie=UTF-8&wd=ios

    • https就是协议,也就是scheme
    • www.baidu.com 是域名
    • /s是路径
    • ?后面的是query,也就是查询参数。这个url有两个参数,分别是ie=UTF-8wd=ios

    我们iOS的URL Schemes中也是差不多的。
    而且,在openURL的时候,如果url中带有参数,只要URL Schemes是正确的,那同样可以打开App,而且,后面的参数也会带到我们打开的App那里。
    咱们做个Demo就一目了然了。
    在MyApp中,写个demo3方法,url为weixin://www.shixueqian.com/abc?title=hello&content=helloworld

    - (IBAction)openWXApp:(UIButton *)sender {
    //    [self demo1];
    //    [self demo2];
        [self demo3];
    }
    //使用URL Schemes传递数据
    - (void)demo3 {
        //创建一个url,这个url就是WXApp的url,记得加上://
        NSURL *url = [NSURL URLWithString:@"weixin://www.shixueqian.com/abc?title=hello&content=helloworld"];
        //打开url
        [[UIApplication sharedApplication] openURL:url];  
    }
    

    在WXApp的AppDelegate.m中,实现application: openURL:(NSURL *)url sourceApplication: annotation:回调

    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
        
        NSLog(@"url=====%@ \n  sourceApplication=======%@ \n  annotation======%@", url, sourceApplication, annotation);
        return YES;
    }
    

    run了之后,我们发现,我们依旧可以通过点击openURL:方法打开WXApp。而且在WXApp被打开的时候,会执行application: openURL:(NSURL *)url sourceApplication: annotation:回调方法。在这个回调方法中,我们可以得到MyApp传过来的url等信息。
    控制台打印如下:

    log结果

    完整的url信息都传过来了,我们就可以利用这个url里面的路径和参数等信息了,想干嘛就干嘛。这就实现了从MyApp向WXApp传递数据了。

    备注:
    苹果一共给了3个openURL的回调。
    分别是:

    - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url NS_DEPRECATED_IOS(2_0, 9_0, "Please use application:openURL:options:") __TVOS_PROHIBITED;
    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation NS_DEPRECATED_IOS(4_2, 9_0, "Please use application:openURL:options:") __TVOS_PROHIBITED;
    - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options NS_AVAILABLE_IOS(9_0); // no equiv. notification. return NO if the application can't open for some reason
    

    为什么会有3个呢?这3个回调又有什么区别?(为方面讲解,分别设置ABC3个回调)

    • 3个回调的功能基本一样,都是在别人通过URL Schemes打开应用的时候会执行的。
      不同之处:
    • A回调是在iOS2.0的时候推出的,参数只有url
    • B回到是在iOS4.2的时候推出的,参数有url sourceApplication annotation.
    • C回调是iOS9.0的时候推出的,参数有url optionsoptions有下面几个key
    // Keys for application:openURL:options:
    UIKIT_EXTERN NSString *const UIApplicationOpenURLOptionsSourceApplicationKey NS_AVAILABLE_IOS(9_0);   // value is an NSString containing the bundle ID of the originating application
    UIKIT_EXTERN NSString *const UIApplicationOpenURLOptionsAnnotationKey NS_AVAILABLE_IOS(9_0);   // value is a property-list typed object corresponding to what the originating application passed in UIDocumentInteractionController's annotation property
    UIKIT_EXTERN NSString *const UIApplicationOpenURLOptionsOpenInPlaceKey NS_AVAILABLE_IOS(9_0);   // value is a bool NSNumber, set to YES if the file needs to be copied before use
    
    • 这几个回调是有优先级的。C>B>A。也就是说,如果你3个回调都实现了,那么程序只会执行C回调。其他回调是不会执行的。(当然,iOS9以下只会执行B回调)。

    参考

    本篇文章的Demo已经上传到GitHub上了https://github.com/shixueqian/OpenApp

    欢迎观看我的另一篇文章,是这篇文章的进阶版。
    【iOS开发】仿微信分享功能

    谦言万语

    用通俗的语言,讲述动人的代码故事。

    相关文章

      网友评论

      • 翻墙的猪:请问大佬,我的app里面的URL Schemes要用http。怎么才能从safari打开我的app呢,因为链接是固定的,不能改。。必须是http开头。:sob:
      • 微笑中的你:请教个问题,如何通过代码动态设置 LSApplicationQueriesSchemes 这个数组值。。。我的app可能打开很多App。。。
        谦言忘语:暂时没什么办法。
        话说,Info.plist里面的东西,在APP运行的时候是无法修改的,因为这个文件在安装路径里面,我们是没有权限操作安装路径里面的任何东西的。我们平常操作的是沙盒路径,这个沙盒路径跟安装路径不是一回事来的。
      • __silhouette:感谢分享!
      • 寻找最亮的光:我这边使用9.3版本的手机可以在回调函数中得到参数并跳转对应界面,但是在最新的系统版本中获取到了参数,但是没有跳转是为什么
      • 洁简:URL里面不能有:号 怎么解决呢
        寻找最亮的光:使用decode和encode方法
      • 翻滚的炒勺2013:在url里取出参数有好的办法吗锕
        koreadragon:rangeOfString
      • BlueFriends:怎么获取自己app的 schemes
      • 令__狐冲:关于两个应用有相同的url scheme,后者覆盖前者。这个我不知道之前是怎么样的,现在是会跳到先安装的app。也就是后面安装的,即使有相同的也不会跳过去。
        梦北_向南:记得一直是优先跳到新安装的版本的
        令__狐冲:@谦言忘语 嗯,我是看到最近在支付宝里面点击淘宝,跳转到的是易车。所以试验了下如果两个app有相同的url scheme,优先跳哪个。
        谦言忘语:终于有人注意到这个问题了。我后面试了下,确实文章的推测是不太合理的。
      • PGOne爱吃饺子:讲的真不错,收藏了,受用了
      • 七郎PK陈雷:打赏了一点小意思 希望能联系一下 交易单号:4200000079201802067902743344
        扣扣209 060 7631
      • Timeless_a25f:可以加我扣扣吗969268210
      • Timeless_a25f:为什么我的canOpenUrl不管有没有装微信返回的都是ture啊 急死我了
        梦北_向南:你是用自己APP的scheme去查的吧?
        谦言忘语:@Timeless_a25f 那就没装微信的时候打开看看,看打开的是什么玩意
      • OBJECT_C:你这个是必须要运行B项目,A项目才能调起B项目,如果不运行B项目,如何在A项目里跳转B项目
        OBJECT_C:@谦言忘语 这样可以实现吗,我现在有个需求就是有两个APP,B项目要放到A项目,前提是用户不用安装 B项目也可以跳转B项目
        OBJECT_C:@谦言忘语 我的意思是B项目不安装,只把B项目的代码放去,B项目不做任何操作
        谦言忘语:@OBJECT_C 只要安装了就可以。关闭了B项目也可以的。
      • OBJECT_C:我想问下,如何在不安装B项目的情况启动B项目???
        谦言忘语:@OBJECT_C 不安装?那肯定是不行的
      • 莫云溪:遇到了openURL优先级问题,感谢作者!
      • zziazm:打开我的微信时那个alert不是每次都出现的吗
      • d551c712efbe:可以将URL分享在微信中,然后用户在微信中点击URL打开APP么?
        谦言忘语:@不君子 目前这种方式已经被微信屏蔽了,使用iOS9之后的Universal link可以
      • 水寒不知:这个必须要wxapp已经打开了。才能执行回调。
        c170dc2aa789:老哥,未打开APP然后通过外部链接打开启动后..走的是哪个方法啊
      • JHA:楼主你好 如果俩个应用的Schemes一样 我要怎么区分啊
        谦言忘语:@JHA 那先先使用CanOpenURL方法判断飞猪是否安装
        JHA:@谦言忘语 我现在做的是跳淘宝详情页(是完全可以跳转的) 但是如果安装飞猪的话 就会跳到飞猪里面
        谦言忘语:如果是别人打开你的APP的话,handleOpenURL方法会有个sourceApplication参数,这个是APP的bundleID。
        如果是你打开别人的APP的话,而有两个APP URLSchemes相同的话,嗯,我也不知道怎么处理,不过一般都会把Schemes弄成唯一的
      • 刘超_a594:你好为什么吊起未打开的app不跑openurl的方法呢
        c170dc2aa789:@谦言忘语 我也碰到这种问题,外部链接跳转 打开未启动的app,怎么知道调了哪个方法,app没启动没法调试很无奈
        谦言忘语:会走的。
        你应该是没看到log吧,没有打开的APP在Xcode里面是看不到log的。
        你试着用KKlog这个库将log打印到沙盒中看下
      • df0fa4542909:如果用户点击了取消打开的话,有回调吗?
        谦言忘语:没有回调,没有任何效果
      • fc1df54ed914:如果要跳转到某个具体的页面,该怎么做呢
        谦言忘语:urlscheme后面加参数,使用获取到的参数进行跳转
      • 番茄炒西红柿啊:写的真详细,很喜欢
      • 攻城:通俗易懂:+1:
      • ihere洋:我按照你的方法做的,为什么第一次打开时没有出现弹框呢?
        0939513fb4e7:@专治闹20年 应该是 weixin:// 吧
        1cd3909eb544:楼主你好!文章写得很棒!
        我有个问题,在ios原生浏览器里面输入了wechat:// 然后提示我“是否打开微信”,我点是,之后却又 弹窗提示网址无效,无法打开该网页呢?
        谦言忘语:也许你以前打开过。置模拟器试试
      • 刘书亚的天堂之路:写得真棒,3qu
      • Linton:用通俗的代码,讲述动人的故事O(∩_∩)O哈哈~
      • c1482b09ec8a:有个问题想问下, [WXApi registerApp:@"wx*********" ];调起支付得先注册,注册的APPID和scheme填的要一样才能调起支付,支付完正常跳回。这样的话,那不是就得一个应用申请一次微信id,无法公用了喽,这样对于要出多个包的就很伤了,每次都得申请,有没有方法只申请一个微信APPID,用于多个包的方法呢?
        elite_kai:有方法,但是支付完成以后不能返回自动返回app,需要手动返回app,然后调用后台接口,让后台查询支付订单返回给你结果
        谦言忘语:这个我不太清楚,支付的话,微信应该会验证bundleID的,那就要一个appid对应多个bundleid,不知道微信有没有提供这种配置
      • YANGWW:请问回调application: openURL: options:返回YES和NO,有什么区别?
      • LoveY34:楼主!你好!我一直搞不懂一个问题就是这个方法- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options NS_AVAILABLE_IOS(9_0)的返回值到底有什么意义,api备注是:return NO if the application can't open for some reason,但是我试了不管return的YES/NO,应用程序都会打开的,感觉没啥用啊?还是说我弄错了!
        sincere_bs:@茗玥古城 确实没啥意义 我也试了。。
        5dbca6037233:同问,感觉完全没意义
      • fuadam1982:微信、微博这些app都是可以自动返回到调用app的,这个怎么实现的?
        谦言忘语:@fuadam1982 ...文章上已经说了,只是对caOpenURL有限制,对openURL是没有限制的。微信肯定敢直接openURL,因为URLSchemes你肯定已经按照微信的规则配好了(如果你没有配置wx123456这种URL的话微信分享SDK会让你编译不过的)。所以,你的URLSchemes微信肯定是知道的,不管是直接通过你OpenURL的时候传过来,还是根据你的BundleID从服务器读取,总之,微信肯定能知道。
        fuadam1982:@谦言忘语 iOS 9中对URL scheme有限制的。你自己的app中配置的一般是这样的:wx1234567890. 所以微信这类app是无法知道具体你app的url scheme,且不能动态配置info.plist。所以openURL是无法使用的。
        谦言忘语:@fuadam1982 具体我没怎么研究,但是分享完成之后回到我们的app,这个功能肯定是微信客户端自己实现的。我们app里面集成了微信的分享SDK,也就拥有了分享到微信的功能。当我们打开微信,并将数据传给微信的时候,微信客户端自动会处理这个分享操作,分享成功之后,微信客户端会弹出一个提示框,提示是回到app还是留在微信。也就是说,正常微信分享有两个必要条件,一你的app集成了微信SDK,用来打开微信并传递数据给微信。二微信客户端有能力处理来自你app的请求。如果微信没有处理分享功能的代码,是无法进行分享的。至于返回到app,很简单,你只需要将微信的APPID传给微信就好了,微信会使用openURL来打开app。
      • karen我心永恒:真的是通俗易懂,不错不错!
      • YY_Lee:看了好多篇博客,就看你的看会了:+1:
      • 风吹柳絮如花落:真机safari 掉不起来app
        谦言忘语:@Foundation urlscheme设置了吗?只要设置了就行的
        风吹柳絮如花落:@谦言忘语 但是调不起来demo呀
        谦言忘语:@Foundation 你在真机的Safari输入wechat:// ,就可以把微信调起来
      • 4019f4180e2e:通俗易懂,不错不错 :+1:
        谦言忘语:@一支回旋铅笔 谢谢😝
      • 屈涯:- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {

        NSLog(@"\n url=====%@ \n sourceApplication=======%@ \n annotation======%@", url, sourceApplication, annotation);
        return YES;
        }
        为什么不走Appdelegate里的这个方法
        狂奔的大蜗牛:我今天也遇到这个问题了,后来无意间发现需要MyApp和WXApp同时运行,点击打开微信的时候,才会执行application: openURL:(NSURL *)url sourceApplication: annotation:回调方法
        谦言忘语:@苏_沫 如果你是可以通过MyApp打开了WXApp的话,应该会走的啊。除非你实现了我上面备注说的C方法。
        昵称有毛用:@苏_沫 因为你的应该程序已经打开了 在后台运行 只有应用程序处于未打开的状态才会进这个方法
      • iOS1000:你好,请教一下。如何返回一张WXApp的本地图片给MyApp?
        sunny冲哥:刚看到一个demo,是调用系统剪贴板进行操作的
        谦言忘语:@5ddc84a71156 提供一个思路:将图片转化成data,然后转换成字符串,当做url的参数传过去。然后到WXApp之后,取出这个字符串,转化成data,再转成图片。
      • 冰三尺:这个有意思,这次以前配置微信支付和支付宝支付需要配置scheme,当时就知道这样做,不知道为什么,看了这篇文章,我豁然开朗。
        程序员不务正业:@谦言忘语 图片有个坑,图片转nsdata再转nsstring有时候会变为nil,所以需要先编码,我是通过转base64再encode放到url中。
        谦言忘语:@小飞向前冲 有用就好,哈哈。分享一个网址,iOS9的适配,讲得特别详细,对iOS9的适配会有种让人豁然开朗的感觉。https://github.com/ChenYilong/iOS9AdaptationTips。可惜这个作者iOS10的适配还没出来。
      • 122971e34fcc:有点意思,不错,继续加油
        谦言忘语:@Cristyang 哈哈୧(๑•̀⌄•́๑)૭谢谢鼓励:smile:

      本文标题:【iOS开发】打开另一个APP(URL Scheme与openU

      本文链接:https://www.haomeiwen.com/subject/tbirjttx.html