一、URL Scheme 简介和作用
相信大家都知道 URL,例如 http://www.jianshu.com/ 就是一个URL。
在 ://
之前的部分就称为 URL Scheme。
也就是说 http://www.jianshu.com/ 的 URL Scheme 就是 http 。
更多关于 URL参数介绍:NSURL简介
由于苹果的app都是在沙盒中,相互是不能访问数据的。但是苹果还是给出了一个可以在app之间跳转的方法:URL Scheme
。
简单的说,URL Scheme 就是一个可以让 app 相互之间可以跳转的协议(例如上面的 http)。
每个app的 URL Scheme 都是不一样的,如果存在一样的 URL Scheme,那么系统就会响应先安装那个app的 URL Scheme,因为后安装的app的 URL Scheme 被覆盖掉了,是不能被调用的。
那么app之间的跳转有什么作用呢?我们所使用的每一个app就相当于一个功能,app的跳转可以使得每个app就像一个功能组件一样,帮助我们完成需要做的事情,比如三方支付,搜索,导航,分享等。
二、使用URL Scheme 跳转到系统
要跳转到别人的app,就要知道别人的app的跳转协议是什么,需要传入什么参数,我们常见的跳转到系统有下面这些:
// 1.打开Mail
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto:frank@wwdcdemo.example.com"]]
// 2.打开电话
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel:1-408-555-5555"]];
// 3.打开SMS
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms:1-408-555-1212"]];
有关系统支持的URL Scheme的详细信息,请参阅 Apple URL Scheme Reference 和 iOS10如何跳转到系统设置。
三、自定义 URL Scheme 进行跳转
1、注册自定义 URL Scheme
如果我们希望别人打开我们的 app(名字叫做 SchemeDemo),需要注册自定义 URL Scheme,通过 info.plist
--> URL Types
--> item0
--> URL Schemes
--> 你的TestScheme
来设置,详细步骤如下:
-
1、点击工程中的
info.plist
文件,当该文件显示在如下窗口时,在列表顶部鼠标选中Information Property List
,选择+
,然后向下滚动弹出的列表并选择URL types
,类型为NSArray
。
-
2、点击
URL types
左边剪头打开列表,可以看到Item 0
,一个字典实体。展开Item 0
,可以看到URL Identifier
,一个字符串对象。该字符串是你自定义的URL scheme
的名字。建议采用反转域名的方法保证该名字的唯一性,比如com.yourCompany.yourApp
。
-
3、点击
Item 0
新增一行,从下拉列表中选择URL Schemes
,敲击键盘回车键完成插入。注意URL Schemes
是一个数组,允许应用定义多个URL schemes
。
-
4、展开
URL Schemes
该数据并点击Item 0
。你将在这里定义自定义URL scheme
的名字。只需要名字,不要在后面追加://
,比如,如果你输入iOSDevApp
,你的自定义 url 就是iOSDevApp://
。
此时,整个定义如下图:
2、从 Safari 中调用自定义 URL Scheme
使用模拟器调用应用的步骤:
在 Xcode 中运行应用
一旦应用被安装,自定义 URL scheme 就会被注册
通过模拟器的硬件菜单中选择 Home 来关闭应用
启动 Safari
在浏览器地址栏输入之前定义的 URL scheme (如下)
- 点击回车,弹出提示框 (如下)
- 点击
打开
此时 Safari 将会进入后台,应用会被带回到前台。
祝贺你刚刚使用自定义 URL scheme 调用了一个 iPhone 应用。
3、从另一个应用( NewDemo
)中调用( SchemeDemo
中的 )自定义 URL Scheme
新建一个应用 NewDemo
,来调用 SchemeDemo
中自定义的 URL scheme
。
新建应用 NewDemo
只有一个 UIButton
,点击这个按钮则会通过应用(SchemeDemo
)自定义的 URL scheme
来调用应用(SchemeDemo
)。
在按钮点击方法 clickBtn
中代码处理 URL 调用:
- (void)clickBtn {
NSString *urlString = @"iOSDevApp://";
// 若有中文传输需要进行转义
NSString *customURL = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
// 检查自定义 URL 是否被定义,如果定义了,则使用 shared application 实例来打开 URL
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:customURL]]) {
// openURL: 方法启动应用并将 URL 传入应用,在此过程中,当前的应用进入后台
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:customURL]];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"URL error" message:[NSString stringWithFormat:@"No custom URL defined for %@", customURL] delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[alert show];
}
}
如果你的系统版本低于 iOS 9,那么已经可以进行调用了,但是如果是在 iOS 9及以后的系统版本中仍会出现无法调用,并在控制台输出如下错误:
-canOpenURL: failed for URL: "iOSDevApp://" - error: "This app is not allowed to query for scheme iosdevapp"
原因:因为从 iOS 9 开始系统引入了 LSApplicationQueriesSchemes
,就是白名单。
用意:当前App允许访问的App有哪些,需要通信双方均设置为对方的 scheme,否则当调用对方App时,系统会告诉你 This app is not allowed to query for scheme
。
调用者和被调用者均需要设置白名单,一方想调用,另一方需要也知道将被你调用 ,更为安全。
解决办法:此时需要在 NewDemo 和 SchemeDemo 中的 info.plist 里面相互设置为白名单(LSApplicationQueriesSchemes)。详情如下:This app is not allowed to query for scheme
设置好之后重新运行就可以了:点击打开就可以跳转到 SchemeDemo
中了。
4、通过自定义 URL Scheme 向应用传递参数
有时你需要通过自定义 URL 向应用中传递参数。让我们看看该如何完成这个工作。
NSURL 作为从一个应用调用另一个的基础,遵循 RFC 1808 (Relative Uniform Resource Locators) 标准。 因此你所熟悉的基于网页内容的 URL 格式在这里也适用。
在自定义了 URL scheme 的应用中,AppDelegate 必须实现以下方法:
// iOS 9.0前方法
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation;
// iOS 9.0后方法
- (BOOL)application:(UIApplication *)app openURL:(nonnull NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;
从一个应用传递参数到另一个的诀窍是通过 URL。例如,假设我们使用以下的 URL scheme,想传递一个名为 “token”的参数和一个标识注册状态的标志,我们可以像这样创建一个 URL:
// 若有中文传输需要进行转义
NSString *customURL = @"iOSDevTips://?token=123abct®istered=1";
在 web 开发中,字符串 ?token=123abct®istered=1
被称作查询字符串(query string)。
在被调用(设置了自定义 URL)的应用的 AppDelegate 中,获取参数的代码如下:
// iOS 9.0前方法
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
NSLog(@"Calling Application Bundle ID: %@", sourceApplication);
NSLog(@"URL scheme: %@", [url scheme]);
NSLog(@"URL query: %@", [url query]);
return YES;
}
// iOS 9.0后方法
- (BOOL)application:(UIApplication *)app openURL:(nonnull NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
NSLog(@"options: %@", options);
NSLog(@"Calling Application Bundle ID: %@", [options objectForKey:@"UIApplicationOpenURLOptionsSourceApplicationKey"]);
NSLog(@"URL scheme: %@", [url scheme]);
NSLog(@"URL query: %@", [url query]);
return YES;
}
在 iOS 9.0 之后,以上代码在应用被调用时的输出如下:
options: {
UIApplicationOpenURLOptionsOpenInPlaceKey = 0;
UIApplicationOpenURLOptionsSourceApplicationKey = "--.NewDemo";
}
Calling Application Bundle ID: --.NewDemo
URL scheme: iOSDevApp
URL query: token=123abct®istered=1
注意 “Calling Application Bundle ID”,你可以用这个来确保只有你定义的应用可以与你的应用直接交互。
让我们改变一下代码,来验证发起调用的应用的 Bundle ID 是否合法:
// iOS 9.0前方法
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
NSLog(@"Calling Application Bundle ID: %@", sourceApplication);
NSLog(@"URL scheme: %@", [url scheme]);
NSLog(@"URL query: %@", [url query]);
// Check the calling application Bundle ID
if ([sourceApplication isEqualToString:@"--.NewDemo"]) {
return YES;
}
return NO;
}
// iOS 9.0后方法
- (BOOL)application:(UIApplication *)app openURL:(nonnull NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
NSLog(@"options: %@", options);
NSLog(@"Calling Application Bundle ID: %@", [options objectForKey:@"UIApplicationOpenURLOptionsSourceApplicationKey"]);
NSLog(@"URL scheme: %@", [url scheme]);
NSLog(@"URL query: %@", [url query]);
// Check the calling application Bundle ID
if ([[options objectForKey:@"UIApplicationOpenURLOptionsSourceApplicationKey"] isEqualToString:@"--.NewDemo"]) {
return YES;
}
return NO;
}
有一点要特别注意,你不能阻止其他应用通过自定义 URL scheme 调用你的应用,然而你可以跳过后续的操作并返回 NO,就像上面的代码那样。也就是说,如果你想阻止其它应用调用你的应用,创建一个与众不同的 URL scheme。尽管这不能保证你的应用不会被调用,但至少大大降低了这种可能性。