合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
> 原文出处: http://chengway.in/post/ji-zhu/core-data-by-tutorials-bi-ji-wu 我们继续来看[《Core Data by Tutorials》](http://www.raywenderlich.com/store/core-data-by-tutorials)这本书的第七章 Syncing with iCloud,本章所讨论的是iOS 8和Yosemite最新释出的iCloud Drive,至于iCloud Drive与iCloud的区别可以看[这里](http://www.v2ex.com/t/139804),调试本章code需要一个开发者帐号:) ## **Chapter 7: Syncing with iCloud** ### **一、CloudNotes** iCloud是基于Core Data的,之前几个版本确实做的比较烂,不过苹果也在不断地改进。本章的代码其实就是在第六章代码的基础上改为Cloud版本。 ### **二、Enabling iCloud** iCloud对core data store同步其实使用的是**ubiquity containers**(无处不在的容器),这个*无处不在的容器*存在你应用的sandbox中,并由iOS系统替你管理。如同向NSFileManager请求应用的 Documents directory,对于iCloud来说,请求的是一个**ubiquity container URL**。 Core Data通过这个ubiquity container URL保存应用程序的数据,他和通过URL保存到Documents没什么区别。唯一不同的就是iCloud后台进程监视着*ubiquity container*里面文件的变化,时刻准备上传到云端,而这一切都是系统自动完成的。 这里需要注意的是,iCloud的运行机制有点类似与**Git**,每次同步的其实是transaction logs,而不是data store本身。而与Git不同的是,commit的时候,Git经常需要处理冲突。但iCloud的commit change是“atomic”的,意味着,要么数据全部有效,要么全部丢弃。 作者这里用伦敦桥搬家举了一个很形象的例子,把桥拆成一砖一砖,然后搬到目的地再按照log记录的正确顺序重组。iCloud的工作机制也差不多。 在Xcode中将Capabilities中的iCloud启用。这里可以使用默认的ubiquity container,也可以自定义。 ![](https://box.kancloud.cn/2015-08-21_55d6eb3414c7d.jpg) ### **三、The cloud stack** 为**.addPersistentStoreWithType方法的参数***option数组*增加一个成员**NSPersistentStoreUbiquitousContentNameKey**,同样地在stack的初始化中设置。 ~~~ lazy var stack : CoreDataStack = { let options = [NSPersistentStoreUbiquitousContentNameKey: "CloudNotes", NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true] let stack = CoreDataStack(modelName: "CloudNotesDataModel", storeName: "CloudNotes", options: options) return stack }() ~~~ 使用NSPersistentStoreUbiquitousContentNameKey的值*CloudNotes*,在ubiquity container中来唯一标识该应用的persistent store。 到此为止,已经为该APP完全开启了iCloud同步,很简单吧。但Core Data在背后还是做了一些工作的,其中一项就是设置了一个**fallback store**,用于在iCloud离线时保存数据。不过开发者完全不用担心这些东西。 ### **四、Testing iCloud** 至于测试则需要拥有一个开发者帐号,登录[itunesconnect](https://itunesconnect.apple.com/),依次选择**Users and Roles** >> **Sandbox Testers** 新建一个测试用户,这里注意的是,新建完的测试帐号需要在真机设备上激活一下。 具体的测试也很简单,现在模拟器上运行起来,可以看到目前所有添加的数据,然后切换target device(并不按下停止键)选择真机设备Run一下,此时模拟器和真机会同时运行。此时在模拟器上创建新的记录,并选择Debug\Trigger iCloud Sync来触发同步,不久应该就能看到新添加的记录在真机上出现。作者还展示了**iCloud gauge**的方式来查看具体的同步记录。 现在可以来总结一下设置iCloud的基本步骤了,主要有三步: 1. 启用iCloud并且设置ubiquity containers。 2. 通过设置persistent store的options来启用 iCloud-Core Data同步。 3. 当app运行时,设置app响应新的变化。 前两步已经说过了,现在来看第3点。 ### **五、Responding to iCloud changes** 该实例程序使用的是fetched results controller,而fetched results controller主要又依赖的是NSManagedObjectContext。但是iCloud更新是直接在persistent store级别的,不会经过Context。因此也不会触发fetched results controller的代理方法来更新UI。 既然如此,我们需要知道iCloud何时更新可以通过监听广播的方法来实现,通过监听**NSPersistentStoreDidImportUbiquitousContentChangesNotification**广播,来刷新context(通过mergeChangesFromContextDidSaveNotification(notification)) ### **六、Switching iCloud accounts** 当前账户登出的话,Core Data会删除当前数据(都安全保存在云端,会在用户重新登录时同步回来)。帐号切换时,Core Data会发送如下两个通知: * NSPersistentStoreCoordinatorStoresWillChangeNotification * NSPersistentStoreCoordinatorStoresDidChangeNotification **the notification**会包含具体要被*adding/added*或*removing/removed*的**NSPersistentStore objects** 1. 当Core Data发出NSPersistentStoreCoordinatorStoresWillChangeNotification时,Core Data stack会保存当前所有数据到当前store中,这样用户帐号登出了不会丢失任何数据。接着重置managed object context。 2. 当Core data发出“did change” notification时, the notes list controller需要重为新登录的帐号的或本地存储的数据源刷新View。 先处理 “will change” notification: ~~~ func persistentStoreCoordinatorWillChangeStores( notification: NSNotification){ var error : NSErrorPointer = nil if context.hasChanges { if context.save(error) == false { print("Error saving \(error)") } } context.reset() } ~~~ 再处理“did change” notification ~~~ func persistentStoreCoordinatorDidChangeStores(notification:NSNotification){ notes.performFetch(nil) tableView.reload() } ~~~