CoreData遇见iCloud的那些坑
创始人
2025-01-11 06:30:30
0次

尽管苹果把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. NSDictionary *storeOptions = 
  2.     @{NSPersistentStoreUbiquitousContentNameKey: @"MyAppCloudStore"}; 
  3. NSPersistentStore *store = [coordinator addPersistentStoreWithType:NSSQLiteStoreType 
  4.                                                      configuration:nil 
  5.                                                                URL:storeURL 
  6.                                                            options:storeOptions 
  7.                                                              error:&error]; 

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

***贴上Swift实现persistentStoreCoordinator的代码:

  1. var persistentStoreCoordinator: NSPersistentStoreCoordinator! { 
  2.    if _persistentStoreCoordinator == nil { 
  3.        let storeURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent("HardChoice.sqlite") 
  4.        var error: NSError? = nil 
  5.        _persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) 
  6.          
  7.        // iCloud notification subscriptions 
  8.        let dc = NSNotificationCenter.defaultCenter() 
  9.        dc.addObserverForName(NSPersistentStoreCoordinatorStoresWillChangeNotification, object: self.persistentStoreCoordinator, queue: NSOperationQueue.mainQueue(), usingBlock: { (note) -> Void in 
  10.            self.managedObjectContext.performBlock({ () -> Void in 
  11.                var error: NSError? = nil 
  12.                if self.managedObjectContext.hasChanges { 
  13.                    if !self.managedObjectContext.save(&error) { 
  14.                        println(error?.description) 
  15.                    } 
  16.                } 
  17.                self.managedObjectContext.reset() 
  18.            }) 
  19.        }) 
  20.        dc.addObserverForName(NSPersistentStoreCoordinatorStoresDidChangeNotification, object: self.persistentStoreCoordinator, queue: NSOperationQueue.mainQueue(), usingBlock: { (note) -> Void in 
  21.            self.managedObjectContext.performBlock({ () -> Void in 
  22.                var error: NSError? = nil 
  23.                if self.managedObjectContext.hasChanges { 
  24.                    if !self.managedObjectContext.save(&error) { 
  25.                        println(error?.description) 
  26.                    } 
  27.                } 
  28.            }) 
  29.        }) 
  30.        dc.addObserverForName(NSPersistentStoreDidImportUbiquitousContentChangesNotification, object: self.persistentStoreCoordinator, queue: NSOperationQueue.mainQueue(), usingBlock: { (note) -> Void in 
  31.            self.managedObjectContext.performBlock({ () -> Void in 
  32.                self.managedObjectContext.mergeChangesFromContextDidSaveNotification(note) 
  33.            }) 
  34.        }) 
  35.          
  36.        if _persistentStoreCoordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: [NSPersistentStoreUbiquitousContentNameKey:"MyAppCloudStore"], error: &error) == nil { 
  37.            println("Unresolved error \(error), \(error?.userInfo)") 
  38.            abort() 
  39.        } 
  40.    } 
  41.    return _persistentStoreCoordinator! 
  42. } 
  43. 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. let containerURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier("iCloud.com.yulingtianxia.HardChoice") 
  2. if containerURL != nil { 
  3.   println("success:\(containerURL)") 
  4. } 
  5. else{ 
  6.   println("URL=nil") 
  7. } 

如果之前没有在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的可以直接把我的那部分源码粘过去用:

 

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
粉嫩如何诠释霸道 东芝M805... “霸道粉”是个什么玩意东芝M805拿过来的时候,笔者扑哧笑了,不是笑这款笔记本,而是笑这款产品的颜色...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
只需三步,让你掌握阿尔法路由器... 请参照以下阿尔法路由器设置方法,通过浏览器进入及设置宽带路由器。经过这三个步骤的设置,基本上能够熟悉...
如何更省钱? 近期特价热门硒鼓... 如今激光打印设备已经成为商务或企业的绝对主力办公设备,但采购者往往忽视了激光打印设备所用的耗材,相对...
全面诠释网络负载均衡 负载均衡的出现大大缓解了服务器的压力,更是有效的利用了资源,提高了效率。那么我们现在来说一下网络负载...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...