簡體   English   中英

如何使用Core Data和iCloud在兩台設備之間同步數據?

[英]How to Sync data between two devices using Core Data & iCloud?

我想要實現的是當用戶在persistent store上創建,更改或刪除數據時,它將與iCloud store同步,然后更新登錄到同一iCloud帳戶的其他設備。

我使用Objective-C資源創建了一個Core Data Stack並嘗試在Swift中編寫自己的Core Data Stack ,但是我在使用登錄到同一個iCloud帳戶的兩個設備同步數據時遇到了問題。

例如,當iDevice A登錄到iCloud時,它會將數據備份到iCloud,但是當iDevice B登錄到iCloud時,應用程序會刪除持久存儲上已有的任何數據以保存iCloud備份,並且不會出現存儲設備之間的任何更改在另一方面,但似乎保存到iCloud商店作為最新的備份,所以如果應用程序被刪除並重新安裝我看到其他設備做的最新備份 - 考慮到這一點,如果iDevice B已經登錄,它除非重新安裝應用程序並且最后一次備份是由其他設備完成的,否則不會使用來自iDevice A的數據。

有沒有人知道我在Core data stack出錯的地方,使用相同的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)

2)在我捕獲通知的函數的代碼中(在本例中為dataUpdated),它在更新顯示以顯示新數據之前,使用以下代碼重置托管objext上下文:

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
if let managedObjectContext = appDelegate.managedObjectContext {
     managedObjectContext.reset()
}

重置托管對象上下文后,我使用以下代碼再次獲取實體:

let fetchRequest = NSFetchRequest(entityName: "ClassNameHere")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as! [ClassNameHere]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM