http://www.tuicool.com/articles/UfI7vy6
注意:一个说法----宿主应用和容器应用是一个东西(container app)
扩展(Extension)是 iOS 8 和 OSX 10.10 中引入新功能,Extension 不会单独存在,它将做为 App 的附加功能出现,为 App 提供更好的交互体验。由此 Extension 和容器应用(Containing App)之间的数据共享在所难免。Apple 在它的 官方文档 中只是以 NSUserDefaults 举例做了介绍,然后丢了一句“ Use Core Data, SQLite, or Posix locks to help coordinate data access in a shared container.”就算完事儿了。下面就来介绍下具体如何操作。
喵神在 WWDC 2014 Session笔记 – iOS 通知中心扩展制作入门 已经有了非常详尽的介绍,总之你需要在项目中添加一个 Application Extension Target( File > New Target > Application Extension > …),然后就可以从你非常熟悉的 view controller 开始编写 widget 代码了。
在默认情况下,Extension 是无法直接获取 Containing App 的数据的,但在 iOS 8 中我们可以通过开启 App Groups 以实现同一个 team 的 Apps 及其 Extension 之间的数据共享。开启 App Groups:
Capabilities
标签 App Groups
选项并展开,然后戳一下添加按钮 group.xxx
的格式,随后 Xcode 自动创建 .entitlements
授权文件,其中包含了共享容器的访问名称,并将此 App Group 登记在你的开发者账号下的,确保只有你的 team 的 app 可以使用这些共享容器 现在你把原来存在 App 沙箱中的数据改存在 这个 group 中就可以实现数据的共享了,在具体举例之前,你可能发现仅有数据好像还不够,比如你使用了 CoreData,还需要选中 .xcdatamodeld
文件和要用到的 model 文件,在 Xcode 的右侧工具栏中的找到 Target Membership
勾选 Extension 名,把它们加到 Extension Target 中。然后把 .xcdatamodeld
文件加到 Extension 的 Resource Bundle 里面:
Build Phase
标签 Copy Bundle Resources
选项并展开,然后戳一下添加按钮 .xcdatamodel
文件 好了,一切准备就绪,把
[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
换成
[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.xxx"]
也就是让主 App 和 Extension 都去访问共享容器中的文件,从而实现数据的共享。现在“you can also use Core Data, or in some cases SQLite, to help coordinate data access in a shared container.”(捂嘴笑)
如果是 NSUserDefaults
就是把
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
换成
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.xxx"];
http://www.jianshu.com/p/7f8472a97aa6
在整个通信过程中,难点在于宿主应用和应用扩展的数据共享,不仅仅是数据共享,可能还需要共享一些开发文件,比如类文件、xib、storyboard等。不要以为宿主应用和应用扩展同属于一个工程项目,它们两个就可以共同使用项目内的数据和所有文件。这是错误的。那么,宿主应用和应用扩展如何进行数据共享?我们需要创建一个共享域,当然,苹果早就给我们准备好了,我们只需要配置一下即可。
1、配置共享域
(1)配置宿主应用共享域
点击ON后,其实App Groups这里是空的,因为我之前做项目有配置过共享域,所以在选择证书的时候,系统会把证书配置过的共享域都给我自动加载了出来。如果这里是空的,就点击下面的+号,添加一个共享域。
这时,Xcode会弹出提示框,让你给共享库起一个名字以辨别,因为有些项目可能需要不只一个共享域,如果项目支持Apple watch,就需要一个新的共享域支持Apple watch。共享域的名字以group.开头,名字自己起。
选中我们之前在宿主应用创建(选择)的共享域。
OK,应用扩展的共享域配置完毕。
2、数据共享
(1)NSUserDefaults
NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.testAppExtension"];
获取共享域的偏好设置
接下来平时怎么用这里就怎么用。
(2)数据库
在创建应用扩展前,数据库我是放到这个路径下的。
[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"TestDB.sqlite"]
而现在,即使共享域配置完毕,应用扩展继续访问这个路径下的数据库也是访问不到的,因为共享域它有自己的路径。宿主应用和应用扩展之间的空间关系如下:
所以,我们要将数据库放在共享域的路径下。共享域的路径如下:
[[[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.testAppExtension"] absoluteString] stringByAppendingPathComponent:@"TestDB.sqlite"]
通过containerURLForSecurityApplicationGroupIdentifier方法和共享域标识符我们可以获取到该共享域的路径
OK,共享数据到这里暂告一段落。
共享类文件创建一个Framework文件,让Framework文件引用这些需要共享的类,再让宿主应用和应用扩展分别导入Framework文件。这样做就很好的解决了问题,还不容易出错,也便于后期维护。
1、创建framework文件
framework文件的命名规范一些,以Kit为结尾。
创建完framework后工程目录如下
2、引用文件
(1)先把宿主应用target的文件引用删除,因为应用扩展同样要使用FMDB,所以也要把第三方文件从target中删除,否则编译照样会报错。
(2)增加AppExtensionKit的引用文件
需要注意的是,在这里不要添加xib文件,xib在哪修改下面会说。
(3)为应用扩展导入AppExtensionKit文件
添加完后编译一下,报错,40多个。
这是因为应用扩展也要用到libsqlite3.0.tbd这个包,但是并没有为应用扩展添加这个包,所以,重复上面的操作,把libsqlite3.0.tbd加入到AppExtensionKit中。
再编译一下,错误全部消失不见。OK,配置全部完成。
共享xib文件
PasswrodCell是从xib加载的,但我们并没有把xib文件加入到AppExtensionKit中。知道问题出在哪了,去解决。
在宿主应用的target中,找到PasswordCell的引用并删除。如下:
操作完后,xib文件从原来bundle下的路径变成了bundle下AppExtensionKit下的路径。
做完这些还不够,我们还要在ExtensionDemo和PasswordAppExtension两个target下的Copy Bundle Resources中将AppExtensionKit导入进来,否则宿主应用和应用扩展还是用不了PasswordCell.xib。如图:
ExtensionDemo的target:
那我们再次加载Password.xib文件,就需要从Bundle下的AppExtensionKit文件中加载。
加载方式代码如下:
cell = [[NSBundle mainBundle] loadNibNamed:@"AppExtensionKit.framework/ExtensionCell" owner:nil options:nil].lastObject;