我花了4天时间尝试使用iCloud同步为我的Swift 1.2应用程序实现正确的核心数据堆栈,但我真的可以使用一些帮助。
之前,我正在使用从应用程序中的任何位置访问的全局托管上下文;知道这是一个糟糕的实现,现在我添加iCloud同步我决定摆脱它,即使应用程序工作正常。
到目前为止,我已经实现了一个新的,有效的核心数据堆栈,它在设备之间具有不错但不完美的云同步。
现在我面临两个问题:
但是,在解决这些问题之前,我非常感谢 - 如果合适的话 - 对我迄今为止所完成的工作进行了一些验证,这主要是为了确认我写的这些:因为我已经花了很多时间来改变我的核心数据堆栈,所以在前进之前我想知道我是否正确地传播了上下文(我的应用程序的结构没有#&# 39; t符合我在网上找到的任何教程,所以我不得不即兴发挥,或者如果我犯了一些会影响可靠同步或未来发展的基本错误。
我的应用程序结构如下:
UITabBarViewController
作为初始ViewController
UIViewController
(应用启动时显示)UITableViewController
UINavigationController
UITableViewController
嵌入另一个UINavigationController
我有一个 CoreDataStack.swift 类,其代码如下:
import CoreData
@objc class CoreDataStack : Printable {
let context : NSManagedObjectContext
let psc : NSPersistentStoreCoordinator
let model : NSManagedObjectModel
let store : NSPersistentStore?
var description : String {
return "context: \(context)\n" + "model: \(model)"
}
var applicationDocumentsDirectory : NSURL = {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) as! [NSURL]
return urls[0]
}()
init() {
let modelURL = NSBundle.mainBundle().URLForResource("MyDataModel", withExtension:"momd")
model = NSManagedObjectModel(contentsOfURL: modelURL!)!
psc = NSPersistentStoreCoordinator(managedObjectModel: model)
context = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType)
context.persistentStoreCoordinator = psc
let documentsURL = applicationDocumentsDirectory
let storeURL = documentsURL.URLByAppendingPathComponent("MyApp.sqlite")
let options = [NSPersistentStoreUbiquitousContentNameKey: "MyApp", NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
store = psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options, error:&error)
if store == nil {
let dict = NSMutableDictionary()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error
error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict as [NSObject : AnyObject])
println("Error adding persistent store: \(error), \(error!.userInfo)")
abort()
}
}
func saveContext() {
var error: NSError? = nil
if context.hasChanges && !context.save(&error) {
println("Could not save: \(error), \(error!.userInfo)")
}
}
var updateContextWithUbiquitousContentUpdates: Bool = false {
willSet {
ubiquitousChangesObserver = newValue ? NSNotificationCenter.defaultCenter() : nil
}
}
private var ubiquitousChangesObserver : NSNotificationCenter? {
didSet {
oldValue?.removeObserver(self, name: NSPersistentStoreDidImportUbiquitousContentChangesNotification, object: psc)
ubiquitousChangesObserver?.addObserver(self, selector: "persistentStoreDidImportUbiquitousContentChanges:", name: NSPersistentStoreDidImportUbiquitousContentChangesNotification, object: psc)
}
}
func persistentStoreDidImportUbiquitousContentChanges(notification: NSNotification) {
println("Merging ubiquitous content changes")
context.performBlock {
self.context.mergeChangesFromContextDidSaveNotification(notification)
}
}
}
在我的 AppDelegate.swift 中,我在var window: UIWindow?
下添加了以下代码:
lazy var coreDataStack = CoreDataStack()
coreDataStack.updateContextWithUbiquitousContentUpdates = true
// The following code is the way I found to propagate the managed context of the stack instantiated above in all the ViewControllers of the UITabBarController, including those embedded in the two NavigationControllers;
// since in the future I'll probably need some flexibility in term of adding / rearranging the VCs in the TabBar, I kind of like this way to pass around the context.
// I could have also passed the context to the CustomTabBarViewController and from there do the same thing, but I figured I could just pass the context from AppDelegate, since I already can access all the ViewControllers from here with the following code.
var tabBarController = self.window!.rootViewController as! CustomTabBarViewController
for eachViewController in tabBarController.viewControllers! {
if eachViewController.isKindOfClass(CustomViewController){
(eachViewController as! CustomViewController).passedManagedContext = coreDataStack.context // Context is passed to the VC of 1st tab
}
if eachViewController.isKindOfClass(UINavigationController){
var firstNavController = tabBarController.viewControllers![1] as! UINavigationController
for tvc in firstNavController.viewControllers! {
if tvc.isKindOfClass(FirstCustomTableViewController) {
(tvc as! FirstCustomTableViewController).passedManagedContext = coreDataStack.context // Context is passed to the TableVC inside the NavigationController in tab 2
}
}
var secondNavController = tabBarController.viewControllers![2] as! UINavigationController
for tvc in secondNavController.viewControllers! {
if tvc.isKindOfClass(SecondCustomTableViewController) {
(tvc as! SecondCustomTableViewController).passedManagedContext = coreDataStack.context // Context is passed to the TableVC inside the NavigationController in tab 3
}
}
}
}
// Of course, in applicationDidEnterBackground: and applicationWillTerminate: I save the context; obviously, I also save the context, when appropriate, from the other ViewControllers.
有了这个结构,我在AppDelegate中实例化我的堆栈,并从那里将它传播到TabBar的3个元素;从那些,我再次将上下文传播到我出现的每个其他ViewController。我在任何地方都记录了上下文,我可以确认它始终是相同的。
事实上,使用此代码的应用程序可以正常工作。
我无法说这是完美的,因为正如我所说,有时一些物体不会同步,但我怀疑这些物体不同步的原因是另一个(简而言之,我有2 {{子类;子类1的对象具有子类2的对象作为属性;如果我使用现有的subclass2对象作为属性创建新的子类1对象,则同步很好;如果我还创建新的子类2对象,则保存并立即设置它作为subclass1的属性,有时子类2对象不会在另一个设备上同步,而子类1会做,然后错过该属性......我可以在以后处理它。)
在深入研究这个同步问题之前,我真的很想知道我到目前为止用堆栈完成的工作是否有意义,或者它是否很糟糕且需要固定< /强>
然后,如果上面的所有代码都不可怕,并且如果偶然错过同步对象的原因会变成我怀疑的那个,那么另一个问题就出现了,这是一个很大的问题:我是否设置代码来处理用户从iCloud(NSManagedObject
和NSPersistentStoreCoordinatorStoresWillChangeNotification
)登录或退出时发生的通知?
我尝试在我的AppDelegate和我的CoreDataStack类中基于Core Data by Tutorials书,编写了我已经编写的方法(没有实际功能,目前我只是在控制台上记录了一些东西,知道我到了那里),但是在这两种情况下当我在应用程序运行时登录或退出iCloud时,应用程序在控制台中没有一行崩溃,所以我不知道这个问题。
也许我应该把方法放在所有ViewControllers中处理这些通知,因为获取请求发生在那里并且UI从这些类更新,但是我没有传递整个coreDataStack对象,只传递上下文。 ..所以我错过了什么。 我应该通过整个堆栈,而不仅仅是上下文?可以从我的CoreDataStack处理这些通知,还是应该从AppDelegate处理?
真的很感激任何帮助......
提前致谢,请原谅我的问题不清楚(我是一个初学者,英语不是我的主要语言......)。
另外,感谢您抽出时间阅读这个长期的问题!
@ cdf1982
答案 0 :(得分:0)
我认为问题在于iCloud + CD从未正常工作。它不是开发人员代码问题,问题是Apple实现的iCloud + CD只是失败了。