×

iOS应用间通信:URL Schemes

96
fever105
2017.07.26 17:40* 字数 1488

iOS应用间通信:URL Schemes

抛开越狱不谈,URL Schemes几乎是iOS应用间通信(Inter-app Communication)的唯一选择(另一种是Air Drop,但主要用于共享大文件),其重要性毋庸置疑。

更新:Apple在iOS9推出了Universal Links,同样基于URL,力求统一原生应用和web服务的用户体验,可视为URL Schemes的全面升级。当然,其实现也更为复杂。

A. 什么是URL Schemes

URL用于定位资源,譬如网络资源。以下面的URL为例:

http://www.example.com/index.php?key1=value1&key2=value2

根据RFC1808标准,其包含如下组成部分:

内容 角色 作用
http scheme 服务类型。注意,http是一种互联网协议,但理论上任意合法字符串都可以充当scheme
www.example.com host 主机域名
index.php path 资源路径
key1=value1&key2=value2 query 参数

iOS中,你可以为自己的应用定义URL schemes,供外界调用。URL格式必须符合标准(即能够通过NSURL解析)。

总体来说,URL schemes可划分为两类:系统定义&自定义。

B. 系统定义的URL Schemes

有些系统应用天生支持URL schemes,例如电话,邮件,短信,Safari,地图等。

/ scheme 范例 效果 备注
电话 tel tel:16812345678 拨打号码16812345678 电话号码必传
邮件 mailto mailto:frank@163.com frank@163.com写邮件 邮件地址必传
短信 sms sms:16812345678 编写发送给号码16812345678的短信 电话号码不是必传,如不传,则仅打开短信应用
Safari http(s) https://www.baidu.com 在Safari中打开网页 绝大部分http地址都默认使用Safari打开
地图 http(s) http://maps.apple.com/?q=四川菜 搜索附近的四川菜。注意,url含有中文时要编码 地图的scheme并不是map,而是一个host为maps.apple.com的http地址

更多关于系统定义的URL schemes的信息,详见官方文档Apple URL Scheme Reference

C. 调用URL Scheme

调用URL scheme其实很简单,分为两步:

  1. 创建URL;
  2. 要求UIApplication打开它;

注意事项:

  • 调用特定app,必须事先知道其scheme;
  • 除scheme外,有些app还要求传递额外信息;信息错误,可能无法达到预期效果;
  • 自定义schemes与系统schemes发生冲突,默认以后者为准;
  • 多个app注册同一个scheme,调用结果未知;

打开URL的方法如下:

- (void)openURL:(NSURL *)URL completionHandler:(void (^)(BOOL success))completionHandler;
  • 此方法自iOS10引入,低版本请使用openURL:
  • 回调completionHandler携带一个布尔参数success,表示是否成功打开URL。注意,这里的成功意味着有app响应URL scheme,从而被调起;至于URL是否被成功处理,不得而知;
  • 也就是说,只要scheme正确,一定会有app被调起,回调一定显示成功;

我们可以尝试通过Safari调用某个scheme,具体做法为:在地址栏里输入targetScheme://,将targetScheme替换为具体scheme即可。注意,://不可省略;如果包含中文,必须编码。

D. 自定义URL Schemes

自定义URL schemes也可以分为两步:

  1. 注册schemes;
  2. 处理调用请求。

D.1 注册schemes

iOS以URL type为单位管理URL schemes。一个type下可以有多个scheme,但一个scheme只对应一个type。注册URL schemes,实际上是注册URL type。

Info.plist中添加键值对CFBundleURLTypes,其对应一个数组,每个元素都是一个字典,代表一个type。例如:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLIconFile</key>
        <string>iconGinx</string>
        <key>CFBundleURLName</key>
        <string>cn.com.rap.ginx</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>wb2522720237</string>
            <string>wxd3a1541d4423bf8f</string>
            <string>ddyc</string>
            <string>tencent1101352712</string>
            <string>navigationMapBack</string>
        </array>
    </dict>
    <!--其他URL type...-->
<array>

一个URL type字典包含如下键值对:

必填 备注
CFBundleURLSchemes 字符串数组,一个字符串代表一个scheme 一个type下可以有多个scheme
CFBundleURLName type的识别符,必须唯一。推介使用反向DNS风格的命名方式,如com.myhost.myscheme。 具体作用不详
CFBundleURLIconFile type的图标名称 图标用途不详
CFBundleTypeRole app在type中所扮演的角色 具体作用不详,使用默认值即可

更多关于CFBundleURLTypes的信息,详见Information Property List Key Reference中章节CFBundleURLTypes的叙述。

此外,还可以针对scheme定义启动图片。众所周知,app启动时会显示图片。如果app因为响应某个scheme而启动,可以根据scheme定义图片。图片命名格式如下:

<basename> -<url_scheme> <other_modifiers> .png

更多关于URL scheme启动图片的信息,详见App Programming Guide for iOS中章节Displaying a Custom Launch Image When a URL is Opened的叙述。

D.2 处理调用请求

D.2.1 处理逻辑

收到调用请求后,相应的UIApplication代理方法会被调用,所以这里也是处理逻辑的所在:

// UIApplicationDelegate
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options

注意事项:

D.2.2 生命周期

被调用时,app可能处于下列状态之一:

  1. app未运行;
  2. app运行中,但在后台或被挂起;

D.2.2.1 app未运行时被调用

app先启动,再处理请求,但受到下面方法影响:

// UIApplicationDelegate
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
  • 如果任意方法返回NO,则不处理请求(即方法application:openURL:options:不调用);
  • 如果只实现其中一个,则以实现的那个为准;
  • 即使不处理请求,app仍会启动,进入前台;调用者收到成功回调(方法openURL:completionHandler:回调参数显示成功);

D.2.2.1 app运行中被调用

app必定会处理请求,进入前台(即方法application:openURL:options:一定会被调用);

E. LSApplicationQueriesSchemes与canOpenURL:

UIApplication方法canOpenURL:可以判断当前设备上是否有能够响应特定URL的应用。

于是乎,有人利用这个方法过滤大量scheme,判断设备上安装了哪些应用。为防止滥用,自iOS9,Apple要求这个方法只能检测特定名单内的scheme(当然,系统定义的scheme不在此列),开发者需要通过键值对LSApplicationQueriesSchemesInfo.plist中定义这个名单。例如:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>alipay</string>
    <string>tencentweibo</string>
    <string>sinaweibo</string>
    <string>weibo</string>
    <string>mqq</string>
    <string>iosamap</string>
    <string>baidumap</string>
    <string>wechat</string>
    <string>weixin</string>
    <string>sinaweibohd</string>
    <string>weibosdk</string>
    <string>weibosdk2.5</string>
    <string>BestPay</string>
</array>

另外,还要注意:

  • 方法canOpenURL:的返回值仅表示当前设备上是否有能够响应特定URL的应用。并不能反映URL能否被成功处理;
  • 方法openURL:completionHandler:(或openURL:)不受此名单限制;

更多关于LSApplicationQueriesSchemes的信息,详见Information Property List Key Reference中章节LSApplicationQueriesSchemes的叙述。

参考资料

  1. App Programming Guide for iOS
  2. Apple URL Scheme Reference
  3. Information Property List Key Reference
  4. URL Schemes 使用详解
iOS Dev
Web note ad 1