私有API-iOS10 openURL方法跳转到设置界面失效的解决方法

问题

在iOS10之前,跳转到系统设置界面的某个指定界面的方式如下:

1
2
3
4
5
//打开定位服务界面
NSURL*url=[NSURL URLWithString:@"prefs:root=Privacy&path=LOCATION"];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
};

但是在iOS10上,调用canOpenURL:打开系统设置界面时控制台会报如下错误,并且无法跳转:

1
-canOpenURL: failed for URL: "Prefs:root=Privacy&path=LOCATION" - error: "The operation couldn’t be completed. (OSStatus error -10814.)"

原因是iOS10只允许如下方式跳转到设置里自己app的界面,对跳转到其他界面做了限制:

1
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];

解决方法

可以使用MobileCoreServices.framework里的私有API:

1
- (BOOL)openSensitiveURL:(id)arg1 withOptions:(id)arg2;

头文件参考:LSApplicationWorkspace.h
使用方法:

1
2
3
4
//注意首字母改成了大写,prefs->Prefs
NSURL*url=[NSURL URLWithString:@"Prefs:root=Privacy&path=LOCATION"];
Class LSApplicationWorkspace = NSClassFromString(@"LSApplicationWorkspace");
[[LSApplicationWorkspace performSelector:@selector(defaultWorkspace)] performSelector:@selector(openSensitiveURL:withOptions:) withObject:url withObject:nil];

MobileCoreServices.framework不是私有库,所以直接使用performSelector:即可调用私有API。

注意

  • iOS10的系统URLScheme改成了首字母大写,使用小写的方式会无法打开。
  • 使用私有API的app无法通过App Store审核。你也可以尝试把私有类名和selector字符串混淆一下,绕过审核。例如这位仁兄用ASCII混淆的方法:
1
2
3
4
5
6
7
8
9
10
- (UIView *)statusBarView {
UIView *statusBar = nil;
NSData *data = [NSData dataWithBytes:(unsigned char []){0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x61, 0x72} length:9];
NSString *key = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
id object = [UIApplication sharedApplication];
if ([object respondsToSelector:NSSelectorFromString(key)]) {
statusBar = [object valueForKey:key];
}
return statusBar;
}

不过,还是不建议使用私有API,因为它是不可靠的。也许某天苹果就把它移除了。

update:

  • 还有一步遗漏了,app需要添加一个Prefs的URL Schemes,即添加到info.plistLSApplicationQueriesSchemes项中。

参考:

http://stackoverflow.com/a/39102075/6380485
https://www.zhihu.com/question/50635906/answer/125195317
iOS10系统URLScheme