之前把SiriKit的文档啃完了,然后找了很久也没找到完成度比较高的Demo。之前手头上比较忙就先搁置了,最近有时间可以继续了。索性自己摸石头过河,一步步试着实现。
要说SiriKit当然要先了解App Extension了,本篇先介绍一下App Extension。
App Extension
App Extension顾名思义其是对App的扩展,自iOS8开始新增的特性。之前常见有Today(Widget)、Custom Keyboard、Share,iOS9和10之后陆续又增加了不少,如最近比较热的iMessage、Sticker、Content Blocker、Call Directory 以及本次要分享的SiriKit。

特点与区别
App Extension
App Extension在工程中以一个target存在,有自己的info.plist,有自己的配置项,也有自己的bundle id(如:主App的为com.company.xAppName,那么AppExtension通常为com.company.xAppName.xExtensionName)。它的bundle也是独立的,bundle后缀为.appex。
Containing App
我们的主App就是但是Containing App了,实际上App Extension并不是一个独立的App,它不能独立安装与卸载,他是随着主App的安装而安装、卸载而卸载的。而且提交AppStore的时候主App必须要实现一些功能。
Host App
调用Extions的App就是Host App,比如Intent Extension的Host App是Siri。
App Extension是与Host App进行通讯,App Extension与Containing App通过openUrl:进行通讯,而Host App与Containing App之间就没有任何关系了,大致关系如下图:
生命周期
它的生命周期与主App也不同,App Extension是随着用户触发了而创建和启动,用户取消操作或者离开后一段时间他会被系统自动杀死。
数据与代码共享
毕竟App Extension与Containing App并不是一体的,所以数据和代码上的共享是有些需要注意的。
数据共享
开启App Group
首先你需要给需要数据共享的target开启App Group并设置Group Id(只有开发者账号可以进行),注意共享数据的target的group id要勾选一样的。
如下图,我的主App target和Intent Extension的Target开启并设置了GroupId(没有group id需要先点+号增加一个)


NSUserDefaults方式共享
使用NSUserDefaults的初始化方法initWithSuiteName:来获取共享的NSUserDefaults对象,再进行操作
1 | NSString *groupId = @"你取的App Group的Id"; |
NSFileManager方式共享
使用NSFileManager对象的containerURLForSecurityApplicationGroupIdentifier:方法,然后获取共享目录路径,再进行操作,如:
1 | NSString *groupId = @"你取的App Group的Id"; |
使用数据库
使用SQLite、CoreData等数据库进行共享,但是数据库文件还是得靠NSFileManager来读取
主App与Extension各种路径
在主App和Extension中分别打印groupPath、bundlePath和sandyPath,可以发现,它们的groupPath是一样的,bunldePath和sandyPath都不同
1 | //---主App打印 |
代码和图片资源共享
使用Framework
将两边都会用到的代码,打成Framework,然后引入
使用CocoaPods
制作私有CocoaPods库,进行引入。Podfile注意给Intent或者IntentUI的target也加上需要的。如:
勾选target membership
对已有的一些要共用的类在右侧勾选,新建类的时候也考虑是否要勾选。


坑点
报错UserDefaults无法读写
1
2
3
4handle status: 02016-12-15 15:04:17.414706 SendMessageIntentUI[13492:1016495] [User Defaults] Failed to read values in
CFPrefsPlistSource<0x1700e9a00> (Domain: group.com.maihaoche.SiriKitTest,
User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null)):
Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd出现这个错误一般处理:
- group id配置是否错误
- 初始化NSUserDefaults方法错误
使用[[NSUserDefaults alloc] initWithSuiteName:@"xxxx"]
而非[[NSUserDefaults standardUserDefaults] initWithSuiteName:@"xxxx"] - 以上两种均无错的时候,卸载App,clean工程,重新run。但不一定有效。😂
有些属性/方法会标注
NS_EXTENSION_UNAVAILABLE以表明App Extension无法使用