尽管苹果把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

 
 

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

  1. vertx verticle

    以下内容为随手记的,若看客不知鄙人所云,还请原谅则个.............. 公司用的vertx,在国内,这还是款比较年轻的框架,你也可以把他当做一个工具,官网上的说法是: Vert.x is a ...

  2. Android setTag方法的key问题

    android在设计View类时,为了能储存一些辅助信息,设计一个一个setTag/getTag的方法.这让我想起在Winform设计中每个Control同样存在一个Tag. 今天要说的是我最近学习a ...

  3. Vue下路由History mode导致页面无法渲染的原因

    用 Vue.js + vue-router 创建单页应用,是非常简单的.使用 Vue.js ,我们已经可以通过组合组件来组成应用程序,当你要把 vue-router 添加进来,我们需要做的是,将组件( ...

  4. TOJ 4120 Zombies VS Plants

    链接:http://acm.tju.edu.cn/toj/showp4120.html 4120.   Zombies VS Plants Time Limit: 1.0 Seconds   Memo ...

  5. 当Vue中img的src是动态渲染时不显示问题

    最近遇见动态渲染img时,想起了当初刚开始写vue时,曾经遇见的一个小小坑. Vue中:img的src属性是动态渲染时不显示问题1.需求:展示用户头像,数据从后台获取,如果没有拿到则显示默认图片. 如 ...

  6. iOS 面试总结

    APP崩溃 启动秒退 在新 iOS 上正常的应用,到了老版本 iOS 上秒退最常见原因是系统动态链接库或Framework无法找到.这种情况通常是由于 App 引用了一个新版操作系统里的动态库(或者某 ...

  7. SQL条件判断中字符串后面有空格的问题

    也不知何时才有的概念,还是以前一直没有注意,从哪也没有听说过的定义,今天又遇见了一个小坑,特记录下来,防止再陷坑! 才疏学浅,文笔有限,简单点说吧,就是在写SQL Server语句时,以前使用了 WH ...

  8. facebook的infer检测工具的安装

    缘由 由于公司产出代码的时候会使用静态扫描工具检测代码的质量,所以自己就想动手尝试一番infer整个的使用方式和使用效果,便动手安装了infer,结果安装过程中遇见太多的坑,导致很多时候都安装失败,这 ...

  9. 项目兼容ie8技术要点

    好久没有写博客了,因为最近公司项目要调ie8兼容,一直在忙这事,终于竣工了,跟大家分享下这老掉牙的浏览器是如何搞定的...本人新手一枚,欢迎大家指教 项目是使用的jeecg框架,后台使用的java,前 ...

随机推荐

  1. chage 修改用户密码有效期限的命令

    chage 用语法格式: chage [-l] [-m 最小天数] [-M 最大天数] [-W 警告] [-I 失效日] [-E 过期日] [-d 最后日] 用户 前面已经说的好多了,这个只是一笔带过 ...

  2. windows搭建rabbitmq ha

    1.安装erlang22.0 rabbitmq 3.7.15 2.bin下执行命令:rabbitmq-plugins enable rabbitmq_management3.替换.erlang.coo ...

  3. 利用Qt自带工具发布程序

    Qt官方开发环境生成的exe发布方式--使用windeployqt 从开始菜单-->Qt 5.4.0-->5.4-->MinGW 4.9 (32-bit)-->Qt 5.4 f ...

  4. web前端学习(四)JavaScript学习笔记部分(9)-- JavaScript面向对象详解

    1.认识面向对象 1.1.概念 1.一切事物皆是对象 2.对象具有封装和继承特性 3.信息隐藏(类的信息隐藏,包括属性和方法) <!DOCTYPE html> <html lang= ...

  5. 【Django入坑之路】Models操作

    1:字段 AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 pr ...

  6. oracle-约束-序列

    概要图 一 约束 --问题:往表中插入数据的时候,可能出现一些问题,比如:重复插入数据,内容不对(性别) --如何保证数据库表中数据的完整性和一致性呢? 约束是强加在表上的规则或条件,确保数据库满足业 ...

  7. c++的引用用法

    一.引用简介 引用就是某一变量(目标)的一个别名,对引用的操作与对变量的直接操作完全一样. 引用的声明方法:类别标识符&引用名=目标变量名: 例1: int a; int &ra=a; ...

  8. Mac下升级node到最新版本

    建议先装n,再用n把node升级到最新稳定版: $ npm install -g n $ n stable

  9. C# 如何比较版本号大小

    最近遇到了一个数据迁移的问题,因为配置文件的结构发生变化,所以要把低版本的用户数据保存下来,存到最新版本中去. 这里就有一个比较版本号大小的问题了,网上乱七八糟算法一堆,大致意思就是用分割字符串的方法 ...

  10. js中字符串的加密base64

    base64编码主要用在传输,存储表示二进制的领域,还可以进行加密和解密.其实就是字符串的编码和解码 btoa与atob 只能加密ascii,不能加密汉字. var str = 'I LOVE YOU ...