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其实很简单,分为两步:
- 创建URL;
- 要求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也可以分为两步:
- 注册schemes;
- 处理调用请求。
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
注意事项:
- 此方法自iOS9引入,低版本请使用application:openURL:sourceApplication:annotation:
; - url是调用者传入的url,利用NSURL解析,获取所需信息;
- options是字典,通过键值对UIApplicationOpenURLOptionsSourceApplicationKey可以获取调用者的Bundle ID;
- 此方法返回一个布尔值,代表处理成功与否;但不影响调用者的回调结果(方法
openURL:completionHandler:
)。所以这个值的具体作用不详。
D.2.2 生命周期
被调用时,app可能处于下列状态之一:
- app未运行;
- 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不在此列),开发者需要通过键值对LSApplicationQueriesSchemes在Info.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的叙述。