Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/104.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 如何使用核心数据在两台设备之间同步数据&;iCloud?_Ios_Swift_Core Data_Icloud - Fatal编程技术网

Ios 如何使用核心数据在两台设备之间同步数据&;iCloud?

Ios 如何使用核心数据在两台设备之间同步数据&;iCloud?,ios,swift,core-data,icloud,Ios,Swift,Core Data,Icloud,我试图实现的是,当用户在持久存储上创建、更改或删除数据时,它将与iCloud存储同步,然后更新登录到同一iCloud帐户的其他设备 我已使用Objective-C资源创建了一个核心数据堆栈,并尝试在Swift中编写自己的堆栈,但使用登录到同一iCloud帐户的两台设备同步数据时遇到问题 例如,当iDevice A登录到iCloud时,它会将数据备份到iCloud,但当iDevice B登录到iCloud时,应用程序会删除永久存储中已有的任何数据,以保存iCloud备份,存储设备之间的任何更改都不

我试图实现的是,当用户在
持久存储
上创建、更改或删除数据时,它将与
iCloud存储
同步,然后更新登录到同一iCloud帐户的其他设备

我已使用Objective-C资源创建了一个
核心数据堆栈
,并尝试在Swift中编写自己的堆栈,但使用登录到同一iCloud帐户的两台设备同步数据时遇到问题

例如,当iDevice A登录到iCloud时,它会将数据备份到iCloud,但当iDevice B登录到iCloud时,应用程序会删除永久存储中已有的任何数据,以保存iCloud备份,存储设备之间的任何更改都不会显示在另一个设备上,但似乎会作为最新备份保存到iCloud存储中,因此如果删除并重新安装应用程序,我会看到另一个设备所做的最新备份—记住这一点,如果iDevice B已经登录,除非重新安装了应用程序并且最后一次备份是由另一台设备进行的,否则它不会使用来自iDevice A的数据

有人知道我在使用同一iCloud帐户的两台设备之间同步数据的
核心数据堆栈中哪里出错了吗

核心数据堆栈:

// MARK: - Core Data stack
// All the following code is in my appDelgate Core data stack 

func observeCloudActions(persistentStoreCoordinator psc:      NSPersistentStoreCoordinator?) {
// Register iCloud notifications observers for;

//Stores Will change 
//Stores Did Change 
//persistentStoreDidImportUbiquitousContentChanges
//mergeChanges

}

//Functions for notifications

func mergeChanges(notification: NSNotification) {
NSLog("mergeChanges notif:\(notification)")
if let moc = managedObjectContext {
    moc.performBlock {
        moc.mergeChangesFromContextDidSaveNotification(notification)
        self.postRefetchDatabaseNotification()
    }
 }
}

  func persistentStoreDidImportUbiquitousContentChanges(notification: NSNotification) {
self.mergeChanges(notification);
 }    

func storesWillChange(notification: NSNotification) {
NSLog("storesWillChange notif:\(notification)");
if let moc = self.managedObjectContext {
    moc.performBlockAndWait {
        var error: NSError? = nil;
        if moc.hasChanges && !moc.save(&error) {
            NSLog("Save error: \(error)");
        } else {
            // drop any managed objects
        }
        moc.reset();
    }

       NSNotificationCenter.defaultCenter().postNotificationName("storeWillChange", object: nil)

 }
}

 func storesDidChange(notification: NSNotification) {
NSLog("storesDidChange posting notif");
self.postRefetchDatabaseNotification();
//Sends notification to view controllers to reload data      NSNotificationCenter.defaultCenter().postNotificationName("storeDidChange", object: nil)

 }

func postRefetchDatabaseNotification() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
          NSNotificationCenter.defaultCenter().postNotificationName("storeDidChange", object: nil)

 })
 }


// Core data stack 

lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "hyouuu.pendo" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1] as! NSURL
}()

lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("AppName", withExtension: "momd")!
NSLog("modelURL:\(modelURL)")
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)


let documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last as! NSURL


let storeURL = documentsDirectory.URLByAppendingPathComponent("CoreData.sqlite")

NSLog("storeURL:\(storeURL)")

let storeOptions = [NSPersistentStoreUbiquitousContentNameKey:"AppStore"]

var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."

if coordinator!.addPersistentStoreWithType(
    NSSQLiteStoreType,
    configuration: nil,
    URL: storeURL,
    options: storeOptions,
    error: &error) == nil
{
    coordinator = nil
    // Report any error we got.
    let dict = NSMutableDictionary()
    dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
    dict[NSLocalizedFailureReasonErrorKey] = failureReason
    dict[NSUnderlyingErrorKey] = error
    error = NSError(domain: "Pendo_Error_Domain", code: 9999, userInfo: dict as [NSObject : AnyObject])
    // Replace this with code to handle the error appropriately.
    // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
    NSLog("AddPersistentStore error \(error), \(error!.userInfo)")
}

self.observeCloudActions(persistentStoreCoordinator: coordinator)

return coordinator
}()

lazy var managedObjectContext: NSManagedObjectContext? = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil {
    return nil
}
var managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType)
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()

这就是我设置的方式,它同步我的核心数据,并保持更改同步

这是我的AppDelegate提供的

    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
    // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
    // Create the coordinator and store
    var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("APPNAME.sqlite")
    var error: NSError? = nil
    var failureReason = "There was an error creating or loading the application's saved data."
    // iCloud store
    var storeOptions = [NSPersistentStoreUbiquitousContentNameKey : "APPNAMEStore",NSMigratePersistentStoresAutomaticallyOption: true,
        NSInferMappingModelAutomaticallyOption: true]
    // iCloud storeOptions need to be added to the if statement
    do {
        try coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: NSURL.fileURLWithPath(url.path!), options: storeOptions)
    } catch var error1 as NSError {
        error = error1
        coordinator = nil
        // Report any error we got.
        var dict = [String: AnyObject]()
        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)
        // Replace this with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog("Unresolved error \(error), \(error!.userInfo)")
        abort()
    } catch {
        fatalError()
    }

    return coordinator
}()

    // MARK: - iCloud
// This handles the updates to the data via iCLoud updates

func registerCoordinatorForStoreNotifications (coordinator : NSPersistentStoreCoordinator) {
    let nc : NSNotificationCenter = NSNotificationCenter.defaultCenter();

    nc.addObserver(self, selector: "handleStoresWillChange:",
        name: NSPersistentStoreCoordinatorStoresWillChangeNotification,
        object: coordinator)

    nc.addObserver(self, selector: "handleStoresDidChange:",
        name: NSPersistentStoreCoordinatorStoresDidChangeNotification,
        object: coordinator)

    nc.addObserver(self, selector: "handleStoresWillRemove:",
        name: NSPersistentStoreCoordinatorWillRemoveStoreNotification,
        object: coordinator)

    nc.addObserver(self, selector: "handleStoreChangedUbiquitousContent:",
        name: NSPersistentStoreDidImportUbiquitousContentChangesNotification,
        object: coordinator)
}

我认为对我有效的代码与您的代码之间的区别在于:

1) 我看不到您在何处添加了NSPersistentStoreDidImportUbiquitousContentChangesNotification的观察者,例如在代码中:

let nc : NSNotificationCenter = NSNotificationCenter.defaultCenter();
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
nc.addObserver(self, selector: "dataUpdated:",
        name: NSPersistentStoreDidImportUbiquitousContentChangesNotification,
        object: self.managedObjectContext?.persistentStoreCoordinator)
let fetchRequest = NSFetchRequest(entityName: "ClassNameHere")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as! [ClassNameHere]
2) 在捕获通知的函数中的my code(在本例中为DataUpdate)中,在更新显示以显示新数据之前,它使用以下代码重置托管ObjText上下文:

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
if let managedObjectContext = appDelegate.managedObjectContext {
     managedObjectContext.reset()
}
重置托管对象上下文后,我使用以下代码再次获取实体:

let nc : NSNotificationCenter = NSNotificationCenter.defaultCenter();
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
nc.addObserver(self, selector: "dataUpdated:",
        name: NSPersistentStoreDidImportUbiquitousContentChangesNotification,
        object: self.managedObjectContext?.persistentStoreCoordinator)
let fetchRequest = NSFetchRequest(entityName: "ClassNameHere")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as! [ClassNameHere]

我建议您尝试下载下面的示例应用程序,看看是否可以让它们正常工作-如果您有任何问题,请告诉我,我会帮助您。一旦你让他们工作,你可以尝试找出你做了什么不同,并相应地更新-有很多事情可能会导致你的问题。记住从早期版本开始,以避免在开始时过于复杂@DuncanGroenewald感谢您提供的资源,我一直在浏览各种项目文件并研究您的工作-您做得非常好!!作为可用的最佳资源,苹果没有帮助新的开发者。我已经下载了“CoreDataStackManager.swift”文件,但不确定如何在我的项目中使用它,我有一个用于swift 2.0的新核心数据应用程序来测试您的代码。我应该用core文件替换core数据堆栈还是创建实例来使用这些函数?(我是一名新的iOS开发人员)我在这里为示例应用程序的纯Swift版本创建了一个存储库。当你这么说你的代码时,你指的是谁的代码?我指的是JKSDEV的代码。您的(MwcsMac)示例代码确实包含通知注册。我认为最有可能的问题是数据需要重置、重新获取和显示更新。我非常感谢您的回答,很高兴它出现在Swift 2中!!但是使用Swift 1.1 atm,仍然在转换我的代码(150+错误),你在哪里调用你的函数来注册通知?你的应用程序在应用程序商店中吗?你有这些问题吗?如果不是,我强烈建议现在就转到Swift 2。因为你必须重新开始修理一些东西。我提供的代码就是让核心数据同步所需的全部代码。Xcode 7/Swift 2和iOS 9将在不到一周的时间内发布。你是对的,我已经开始转换我的项目,不,我计划在iOS9发布时发布应用程序-如果我和iCloud一切顺利的话。当你说同步时,如果我在一台设备上删除数据,它会在另一台设备上删除数据,因为它们应该使用相同的云持久存储吗?另外,您在哪里调用通知函数?如果添加一个,它会显示在另一个上;如果删除,它会从另一个上删除。我已经为WillChange、DidChange和handleStoreChanges创建了函数,但是什么是WillRemove通知??我找不到使用苹果文档的示例