Extension 与主app共享数据

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.”就算完事儿了。下面就来介绍下具体如何操作。 

Extension Target

喵神在 WWDC 2014 Session笔记 – iOS 通知中心扩展制作入门 已经有了非常详尽的介绍,总之你需要在项目中添加一个 Application Extension Target( File > New Target > Application Extension > …),然后就可以从你非常熟悉的 view controller 开始编写 widget 代码了。 

App Groups

在默认情况下,Extension 是无法直接获取 Containing App 的数据的,但在 iOS 8 中我们可以通过开启 App Groups 以实现同一个 team 的 Apps 及其 Extension 之间的数据共享。开启 App Groups:

  • 在 Xcode 6 的 Targets 中选中主 App‘s Target,找到 Capabilities 标签 
  • 找到 App Groups 选项并展开,然后戳一下添加按钮 
  • 给你的 App Groups 起个名字,通常是 group.xxx 的格式,随后 Xcode 自动创建 .entitlements 授权文件,其中包含了共享容器的访问名称,并将此 App Group 登记在你的开发者账号下的,确保只有你的 team 的 app 可以使用这些共享容器 

  • 选中 Extension’s Target,重复以上操作,勾选刚才创建的 group 就可以了

现在你把原来存在 App 沙箱中的数据改存在 这个 group 中就可以实现数据的共享了,在具体举例之前,你可能发现仅有数据好像还不够,比如你使用了 CoreData,还需要选中 .xcdatamodeld 文件和要用到的 model 文件,在 Xcode 的右侧工具栏中的找到 Target Membership 勾选 Extension 名,把它们加到 Extension Target 中。然后把 .xcdatamodeld 文件加到 Extension 的 Resource Bundle 里面: 

  • Targets 中选中 Extension‘s Target,找到 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)配置宿主应用共享域

Extension 与主app共享数据_第1张图片

点击ON后,其实App Groups这里是空的,因为我之前做项目有配置过共享域,所以在选择证书的时候,系统会把证书配置过的共享域都给我自动加载了出来。如果这里是空的,就点击下面的+号,添加一个共享域。

这时,Xcode会弹出提示框,让你给共享库起一个名字以辨别,因为有些项目可能需要不只一个共享域,如果项目支持Apple watch,就需要一个新的共享域支持Apple watch。共享域的名字以group.开头,名字自己起。

Extension 与主app共享数据_第2张图片

OK,添加完共享域后,新的共享域就出现在了APP Groups中,选中它。

到这里,宿主应用的共享域配置告一段落。

(2)配置应用扩展

Extension 与主app共享数据_第3张图片
点击ON后,系统会弹出提示框,让你选择证书,因为共享域是在证书的基础上配置的。证书选择后,会把对应的所有共享域显示在App Groups中。

Extension 与主app共享数据_第4张图片

选中我们之前在宿主应用创建(选择)的共享域。

OK,应用扩展的共享域配置完毕。

2、数据共享
(1)NSUserDefaults

NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.testAppExtension"];
获取共享域的偏好设置

接下来平时怎么用这里就怎么用。
(2)数据库
在创建应用扩展前,数据库我是放到这个路径下的。

[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"TestDB.sqlite"]

而现在,即使共享域配置完毕,应用扩展继续访问这个路径下的数据库也是访问不到的,因为共享域它有自己的路径。宿主应用和应用扩展之间的空间关系如下:

Extension 与主app共享数据_第5张图片

所以,我们要将数据库放在共享域的路径下。共享域的路径如下:

[[[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.testAppExtension"] absoluteString] stringByAppendingPathComponent:@"TestDB.sqlite"]

通过containerURLForSecurityApplicationGroupIdentifier方法和共享域标识符我们可以获取到该共享域的路径

OK,共享数据到这里暂告一段落。

共享类文件

创建一个Framework文件,让Framework文件引用这些需要共享的类,再让宿主应用和应用扩展分别导入Framework文件。这样做就很好的解决了问题,还不容易出错,也便于后期维护。

1、创建framework文件

Extension 与主app共享数据_第6张图片

framework文件的命名规范一些,以Kit为结尾。

Extension 与主app共享数据_第7张图片

创建完framework后工程目录如下

Extension 与主app共享数据_第8张图片

2、引用文件
(1)先把宿主应用target的文件引用删除,因为应用扩展同样要使用FMDB,所以也要把第三方文件从target中删除,否则编译照样会报错。

Extension 与主app共享数据_第9张图片

(2)增加AppExtensionKit的引用文件

Extension 与主app共享数据_第10张图片

需要注意的是,在这里不要添加xib文件,xib在哪修改下面会说。

(3)为应用扩展导入AppExtensionKit文件

Extension 与主app共享数据_第11张图片

添加完后编译一下,报错,40多个。
这是因为应用扩展也要用到libsqlite3.0.tbd这个包,但是并没有为应用扩展添加这个包,所以,重复上面的操作,把libsqlite3.0.tbd加入到AppExtensionKit中。

Extension 与主app共享数据_第12张图片

再编译一下,错误全部消失不见。OK,配置全部完成。

共享xib文件

PasswrodCell是从xib加载的,但我们并没有把xib文件加入到AppExtensionKit中。知道问题出在哪了,去解决。

在宿主应用的target中,找到PasswordCell的引用并删除。如下:

Extension 与主app共享数据_第13张图片

操作完后,xib文件从原来bundle下的路径变成了bundle下AppExtensionKit下的路径。

做完这些还不够,我们还要在ExtensionDemo和PasswordAppExtension两个target下的Copy Bundle Resources中将AppExtensionKit导入进来,否则宿主应用和应用扩展还是用不了PasswordCell.xib。如图:

ExtensionDemo的target:

Extension 与主app共享数据_第14张图片

那我们再次加载Password.xib文件,就需要从Bundle下的AppExtensionKit文件中加载。
加载方式代码如下:

cell = [[NSBundle mainBundle] loadNibNamed:@"AppExtensionKit.framework/ExtensionCell" owner:nil options:nil].lastObject;


你可能感兴趣的:(扩展,Extension)