CoreData遇见iCloud的那些坑

尽管苹果把iCloud与CoreData之间的完美配合吹的天花乱坠,但在iOS7之前,想用iCloud同步CoreData数据简直就是噩梦,苹果自己也承认了之前的诸多bug和不稳定性,这让苹果不得不重新站出来说他们的工程师已经在iOS7中修复了bug,增强了体验,balabala,关键是对于程序员来说,将iCloud集成到CoreData变得无比简单。

在苹果的官方文档中已经把配置工作叙述的很明确了,简单地说可以总结为三步:

  • 在iTunes Connect创建App ID,在Xcode中找到项目的Capabilities标签并开启iCloud选项。这会为你创建一个默认的iCloud容器,名字格式为“com.XXX.yourAppID”
  • 添加NSPersistentStore时向options参数传入一个持久存储的名称,自己起一个就行,示例代码如下:

1

2

3

4

5

6

7

NSDictionary *storeOptions =

    @{NSPersistentStoreUbiquitousContentNameKey: @"MyAppCloudStore"};

NSPersistentStore *store = [coordinator addPersistentStoreWithType:NSSQLiteStoreType

                                                     configuration:nil

                                                               URL:storeURL

                                                           options:storeOptions

                                                             error:&error];

  • 对NSPersistentStoreCoordinatorStoresWillChangeNotification,NSPersistentStoreCoordinatorStoresDidChangeNotification和NSPersistentStoreDidImportUbiquitousContentChangesNotification这三个通知进行注册以便接收通知后对数据进行处理。最好用NSNotificationCenter的addObserverForName:object:queue:usingBlock:方法来使逻辑更加明确,代码更紧凑。

最后贴上Swift实现persistentStoreCoordinator的代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

var persistentStoreCoordinator: NSPersistentStoreCoordinator! {

   if _persistentStoreCoordinator == nil {

       let storeURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent("HardChoice.sqlite")

       var error: NSError? = nil

       _persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)

       

       // iCloud notification subscriptions

       let dc = NSNotificationCenter.defaultCenter()

       dc.addObserverForName(NSPersistentStoreCoordinatorStoresWillChangeNotification, object: self.persistentStoreCoordinator, queue: NSOperationQueue.mainQueue(), usingBlock: { (note) -> Void in

           self.managedObjectContext.performBlock({ () -> Void in

               var error: NSError? = nil

               if self.managedObjectContext.hasChanges {

                   if !self.managedObjectContext.save(&error) {

                       println(error?.description)

                   }

               }

               self.managedObjectContext.reset()

           })

       })

       dc.addObserverForName(NSPersistentStoreCoordinatorStoresDidChangeNotification, object: self.persistentStoreCoordinator, queue: NSOperationQueue.mainQueue(), usingBlock: { (note) -> Void in

           self.managedObjectContext.performBlock({ () -> Void in

               var error: NSError? = nil

               if self.managedObjectContext.hasChanges {

                   if !self.managedObjectContext.save(&error) {

                       println(error?.description)

                   }

               }

           })

       })

       dc.addObserverForName(NSPersistentStoreDidImportUbiquitousContentChangesNotification, object: self.persistentStoreCoordinator, queue: NSOperationQueue.mainQueue(), usingBlock: { (note) -> Void in

           self.managedObjectContext.performBlock({ () -> Void in

               self.managedObjectContext.mergeChangesFromContextDidSaveNotification(note)

           })

       })

       

       if _persistentStoreCoordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: [NSPersistentStoreUbiquitousContentNameKey:"MyAppCloudStore"], error: &error) == nil {

           println("Unresolved error \(error), \(error?.userInfo)")

           abort()

       }

   }

   return _persistentStoreCoordinator!

}

var _persistentStoreCoordinator: NSPersistentStoreCoordinator? = nil

当然你也可以用lazy关键字同样来实现persistentStoreCoordinator属性的惰性加载。

已经有人将整套CoreData集成iCloud的逻辑抽象出来,比如iCloudCoreDataStack。完全不需要再用宣称能让CoreData与iCloud搭配更简单的第三方库了,因为在iOS7中苹果的确让它简单至极了。

然而当Xcode6和iOS8袭来,一个个坑争先恐后的出现了。

首先是iCloud Drive,它与之前iCloud有冲突。如升级,请彻底,让测试机器都升级iCloud Drive。

然后是Xcode6中开启Capabilities标签的iCloud选项卡后,如下的场景简直是卧槽:

该怎么选怎么选啊?!我只能说按照上图这么选就对了。顺便说一下iCloud默认容器名称格式已经变成了“iCloud.com.yourname.yourAppID”,其实这也不太准确,官方称作“iCloud.$(CFBundleIdentifier)”,后面的美元号所指的变量就是General中Identity一栏的“Bundle Identifier”值。此外“Key-value storage”和“CloudKit”选项选不选都可以,但“iCloud Documents”一定要勾选,否则是无法同步CoreData数据的。

PS:CloudKit是苹果最新推出的基于iCloud的一个云端数据存储服务,提供了低成本的云存储并能作为一个后端服务通过用户们的iCloud账号分享其应用数据。

接下来是时候检查我们是否成功添加了iCloud容器,可以在applicationDidFinishLaunchingWithOptions方法中尝试获取容器的URL来判断:


1

2

3

4

5

6

7

let containerURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier("iCloud.com.yulingtianxia.HardChoice")

if containerURL != nil {

  println("success:\(containerURL)")

}

else{

  println("URL=nil")

}

如果之前没有在Capabilities标签的iCloud中勾选“iCloud Documents”,“URLForUbiquityContainerIdentifier”方法会始终返回nil。来看看苹果开发者论坛上关于这个话题的讨论吧

PS:官方文档不建议在主线程使用URLForUbiquityContainerIdentifier方法,因为它可能需要较长时间来返回URL而阻塞主线程。这里只是为了测试使用。

然而判断iCloud是否真的与CoreData工作正常,苹果的官方文档写的很详细:Using the iCloud Debugging Tools

当我兴致冲冲的打开Xcode中的debug navigator,点击左边的iCloud查看状态时,被眼前的一切惊呆了:

“iCloud Usage”告诉我状态不可用,然而右下角的日志中Using local storage已经从1变成了0,也就是证明了我的APP(HardChoice)已经从CoreData使用本地持久仓库转移到了使用“iCloud-enabled”持久仓库。“Transfer Activity”中柱状图更是显示从iCloud下载了数据。而这其实应该是Xcode6的一个bug,有人已经在苹果开发者论坛讨论了。

根据我的测试,只勾选“Key-value storage”或者在模拟器上调试时,“iCloud Usage”都不会出现。而即使“iCloud Usage”出现了,状态也始终是Disabled,“Transfer Activity”也不是很灵敏。唯独只能相信CoreData的log了。

但我们可以查看“My Mac”的“iCloud Usage”而不是iPhone的“iCloud Usage”:

在“Documents”一栏中可以看出我在两个设备间同步了数据,“mobile”后面跟着的是我的设备编号。展开数据可以看到更详细的同步记录:

虽然通过“My Mac”可以看到iCloud与CoreData的数据同步记录,但是在Xcode6.1.1中“Documents”的显示不是很正常,在最新的Xcode6.2beta版中虽然修复了“Documents”的显示问题,但“iCloud Usage”的种种bug依然存在。

最后,确保网络通常。我在中软实训一个月时,网络奇差,或是屏蔽了iCloud,一直没能调试成功。

贴一张HardChoice同步成功的测试图,因为我是用Swift写的这个Demo,所以喜欢用Swift的可以直接把我的那部分源码粘过去用:

参考:

http://stackoverflow.com/questions/26195612/icloud-debug-gauge-status-disabled

http://stackoverflow.com/questions/25971816/xcode-6-ios-8-icloud-core-data-setup

时间: 2024-10-20 13:46:54

CoreData遇见iCloud的那些坑的相关文章

什么是CoreData?

简介 Core Data是个框架(并不是数据库哦),它使开发者可以把数据当做对象来操作,而不必在乎数据在磁盘中的存储方式.对于iOS程序员来说,这很有用,因为我们已经可以通过代码非常熟悉的操作对象了.由Core Data 所提供的数据对象叫做托管对象(Managed Object),而Core Data本身则位于你的应用程序和持久化存储区(Persistent store)之间.为了把数据从托管对象映射到持久化存储区中,Core Data 需要使用托管对象模型.所有的托管对象都必须位于托管对象上

cocos2dx 3.3rc0 踩坑日记(二)------ HttpClient 的使用

昨天写了Curl的简单使用,遇见了几个坑,今天用HttpClient写样例还是遇见了坑~ 按照网上的资料写总是遇见问题...应该是版本不一样!!!下面我来说说HttpClient的使用方法. 首先要引入头文件和命名空间 #include "network/HttpClient.h" using namespace cocos2d::network; 但是这样还不行,编译的话会报错,无法解析的外部符号....... 出现这个需要添加network库,添加库的方式和以前有点不同.3.x有些

酷课堂iOS交流群,聚集了一群热爱技术、有趣、有料,平均Q龄在10年以上的“老司机”,他们遍布在全国

新书即将上市: 这两天收到出版社的样书,预计这两周将陆续开始上架,感兴趣的小伙伴,到时可在天猫.当当.京东搜索"李发展"即可找到. ? ? ? ? ? ? ? ? ? ? ? ? ? ? 本书内容简介和适合人群: 本书包含Swift 4.0语言.Cocoa Touch.视图控制器.控件.表格.网络.地图.音频.视频.动画.触摸.手势.故事板.CoreData.性能测试.机器学习.增强现实.加密解密等众多功能模块.这些内容是iOS开发必知必会的内容,需要读者重点掌握. 读者对象:通过对本

nexus 从Window迁移至Linux

根据业务需求,需要将nexus-2.8.0从Windows 2003迁移至CentOS 6.8(x86_64)上.在迁移过程中遇见了几个坑,在此记录已做备忘. 因为,nexus是跨平台通用的,所以,我首先在原来的Windows服务器上把nexus打包备份,打包时有两个文件需要打包一个是:nexus-2.8.0-05应用包,另外一个是:sonatype-work文件包.当然这两个包有可能在一个目录下面,但我们之前搭建这个平台的同事是开发,所以......... 在打sonatype-work文件包

Android setTag方法的key问题

android在设计View类时,为了能储存一些辅助信息,设计一个一个setTag/getTag的方法.这让我想起在Winform设计中每个Control同样存在一个Tag. 今天要说的是我最近学习android遇见的setTag的坑.一般情况下我们只需要使用唯一参数的setTag方法.但有时我们需要存储多个数据,所以这个时候我们就需要使用带key的重载. 文档是描述:“ The specified key should be an id declared in the resources of

vertx verticle

以下内容为随手记的,若看客不知鄙人所云,还请原谅则个.............. 公司用的vertx,在国内,这还是款比较年轻的框架,你也可以把他当做一个工具,官网上的说法是: Vert.x is a tool-kit for building reactiveapplications on the JVM. 公司一直用的标准的verticle,因为并没有什么长任务或阻塞任务的接口,所以一直没有使用worker verticle的强烈需求,即使后来简单重构,也只是用注解和反射外加封装,精简了项目

史上最全的IOS电子书PDF制作

本人背靠海量纸质图书,可以制作各种纸质书籍的电子化,有需要可以Q:1481449626 <iOS编程 第3版 针对Xcode 4.3 Big Nerd Ranch培训系列>作者:JoeConway,AaronHillegass著:夏伟频译 页数:589 出版社:武汉市:华中科技大学出版社 出版日期:2013.01 简介:<iOS编程(第3版)>更新了iOS5和Xcode4.3的内容.全书涵盖了开发iOS应用的方方面面.从Objective-C基础知识到新增加的语言特性:从AppKi

iOS知识树,知识目录(包括对象、Block、消息转发、GCD、运行时、runloop、动画、Push、KVO、tableview,UIViewController、提交AppStore)

本文旨在总结iOS知识网络,该知识网络罗列出常见UIKit,Foundation的对象特点和一些使用经验:文本编辑采用树的形式,对知识点进行罗列,并标注一些使用经验(★)希望对初学者有用或给一些解决疑难杂症者提供思路:某些知识点会深入探讨:通过总结希望站在一个较高平台的角度全观Objective-C.知识树中有些是原创文章,有些则是转载网络上iOS大神的文章.笔者会尽量详细的介绍各个知识点.当然一个人的知识面是相当有限的,在给各位读者提供知识参考的同时,欢迎大家对本文提意见. /->UIView

WebView项目总结

本次工作我自己想挑战论坛页的展示部分,因为自己想了解下如何用webview来开发应用. 一.要将一个网页加载到本地展示,就需要本地建立webview的一个模板文件然后解析后台传输过来的html代码,本地模板文件一般置于assets下,其中模板文件包括html模板,html模板中包括的css,js以及image等.为什么选择assets文件夹来存放这些资源,我想是因为在res下面的一些资源文件夹无法以目录结构来显示,而且模板文件根本无须映射到R文件中. 然后需要一个工具类来对后台传送过来的html