之前把SiriKit的文档啃完了,然后找了很久也没找到完成度比较高的Demo。之前手头上比较忙就先搁置了,最近有时间可以继续了。索性自己摸石头过河,一步步试着实现。
要说SiriKit当然要先了解App Extension了,本篇先介绍一下App Extension。
App Extension
顾名思义其是对App的扩展,自iOS8开始新增的特性。之前常见有 Today(Widget)
、 Custom Keyboard
、 Share
,iOS9和10之后陆续又增加了不少,如最近比较热的 iMessage
、 Sticker
、 Content Blocker
、 Call Directory
以及本次要分享的 SiriKit
。
App Extension
在工程中以一个target存在,有自己的 info.plist
,有自己的配置项,也有自己的 bundle id
(如:主App的为 com.company.xAppName
,那么 AppExtension
通常为 com.company.xAppName.xExtensionName
)。它的bundle也是独立的,bundle后缀为 .appex
。
我们的主App就是但是 Containing App
了,实际上 App Extension
并不是一个独立的App,它不能独立安装与卸载,他是随着主App的安装而安装、卸载而卸载的。而且提交AppStore的时候主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
并不是一体的,所以数据和代码上的共享是有些需要注意的。
首先你需要给需要数据共享的target开启App Group并设置Group Id(只有开发者账号可以进行), 注意共享数据的target的group id要勾选一样的。
如下图,我的主App target和Intent Extension的Target开启并设置了GroupId(没有group id需要先点+号增加一个)
使用 NSUserDefaults
的初始化方法 initWithSuiteName:
来获取共享的 NSUserDefaults
对象,再进行操作
NSString *groupId = @"你取的App Group的Id"; NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName: groupId]; //然后正常的取值和赋值就行了 [userDefault setValue:@"value" forKey:@"key" ]; [userDefault valueForKey:@"key"];
使用 NSFileManager
对象的 containerURLForSecurityApplicationGroupIdentifier:
方法,然后获取共享目录路径,再进行操作,如:
NSString *groupId = @"你取的App Group的Id"; NSString *groupPath = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: groupId].path; NSLog(@"groupPath: %@", groupPath); //然后用writeToFile、某个类的WithContentsOfFile之类的操作进行读写就行了
使用SQLite、CoreData等数据库进行共享,但是数据库文件还是得靠 NSFileManager
来读取
在主App和Extension中分别打印groupPath、bundlePath和sandyPath,可以发现,它们的groupPath是一样的,bunldePath和sandyPath都不同
//---主App打印 groupPath: /private/var/mobile/Containers/Shared/AppGroup/4616FF64-1879-4A12-BCC4-C2E7F82C612A bundlePath: /var/containers/Bundle/Application/ED47E5E2-F867-4179-AF99-0609F98CA4DA/SiriKitTest.app sandyPath: /var/mobile/Containers/Data/Application/A79D26BD-B003-4BDB-B3C8-3B73FA7AB08B //---Extension打印 groupPath: /private/var/mobile/Containers/Shared/AppGroup/4616FF64-1879-4A12-BCC4-C2E7F82C612A bundlePath: /private/var/containers/Bundle/Application/03D71C80-B9C0-4480-82A7-044D129614F3/SiriKitTest.app/PlugIns/SendMessageIntentUI.appex sandyPath: /var/mobile/Containers/Data/PluginKitPlugin/E241732D-5383-416C-A411-222B8C780E2B
将两边都会用到的代码,打成Framework,然后引入
制作私有CocoaPods库,进行引入。Podfile注意给Intent或者IntentUI的target也加上需要的。如:
对已有的一些要共用的类在右侧勾选,新建类的时候也考虑是否要勾选。