根据 rfc2396 解决 iOS custom scheme 不生效的问题

问题背景说明

iOS 的自定义方案(custom scheme) 发布已经有4,5年的时间了,随着 iOS 系统的不断演变,在 xcode 配置自定义方案的方法也悄然发生了变化。

在配置的过程中还是会时常出现配置的 scheme 不生效的问题?总结了一下,大致分这几种情况:

首先解释一下应用场景,Apple 设想的场景是希望 app 可以像网页一样被简单的调用打开,同时以一种和网页兼容的方式进行交互。在这个场景中有两个角色,一个是被调用方,也就是提供某个方案及功能的 App A,另一个是调用方,在 iOS 系统中可以是一个 web 链接,也可以是另一个 App ,对于 web 链接来说它的App就是内置的 Safari 浏览器,总之也是另一个 App B。

1.调用方未设置匹配的权限。

首先,我们需要在 App A 项目中相应 TargetInfo 选项页中的 URL Types 中添加一项,设置好 identifier ,这一项尽量使用很难重复的命名规则,比如反向的域名;然后需要设置好你希望用到的方案名称:比如recognize

这时如果通过 [[UIApplication sharedApplication] openURL:@"recognize://audio_snippet" options:nil completionHandler:NULL] 进行 App 功能调用,系统会提示你 This app is not allowed to query for scheme recognize,一般会通过 canOpenURL 方法进行预先检测,避免出现无法预料的情况。

其次,所以我们还需要在调用方 App B项目中的 Info.plist 文件中,增加一项LSApplicationQueriesSchemes 并将其数据类型设置为 Array,在其中增加一个字符串类型值,在这里就是 App A 中设置的 recognize。表明操作系统允许 App B 查询该方案。

2.调用方的 scheme 和被调用方不一致。

最后,还要保证我们通过 openURL 方法进行调用时的方案名称和 App A 的约定是一致的,否则也无法完成跨 App 调用。

3.最后一种情况是不太容易被查明原因,也就是 scheme 在不经意间使用了非法字符。

说到这里,需要提一下 “统一资源标识符通用语法” [Uniform Resource Identifiers (URI): Generic Syntax],这个申明中详细解释了 URI 的语法构成。查阅申明,我们可以看到3.1 片段是关于方案组成的说明,表明方案的命名在 (RFC 2396)[http://www.ietf.org/rfc/rfc2396.txt] 中已经做出了明确的规定,必须以小写字母开头,数字,以及 +, -, . 这三种标点符号的组合才是合法的方案名。

所以如果你还在苦恼为何自己的方案不起作用,可以从这三方面开始找原因。

总结

解决这个问题的思路:1.查阅Apple 官方文档,弄明白自定义方案的意图和应用场景;2.根据官方文档进行配置;3.对相关的概念(URI scheme 的定义)进行系统的分析。

以下是摘取的协议片段:

3.1. Scheme Component

   Just as there are many different methods of access to resources,
   there are a variety of schemes for identifying such resources.  The
   URI syntax consists of a sequence of components separated by reserved
   characters, with the first component defining the semantics for the
   remainder of the URI string.

   Scheme names consist of a sequence of characters beginning with a
   lower case letter and followed by any combination of lower case
   letters, digits, plus ("+"), period ("."), or hyphen ("-").  For
   resiliency, programs interpreting URI should treat upper case letters
   as equivalent to lower case in scheme names (e.g., allow "HTTP" as
   well as "http").

      scheme        = alpha *( alpha | digit | "+" | "-" | "." )

   Relative URI references are distinguished from absolute URI in that
   they do not begin with a scheme name.  Instead, the scheme is
   inherited from the base URI, as described in Section 5.2.