简体   繁体   English

使用 Swift 将现有项目的本地核心数据存储迁移到 iCloud

[英]Migrate local Core Data Store to iCloud for existing project with Swift

I want to enable iCloud for Core Data for an existing project.我想为现有项目启用 iCloud for Core Data。 The user could use the app from a new iPhone or maybe another with his old data.用户可以使用新 iPhone 或其他 iPhone 上的应用程序使用他的旧数据。 So I have to do a migration of the probably existing store to the new store in iCloud / ubiquitous container.因此,我必须将可能现有的商店迁移到 iCloud/无处不在的容器中的新商店。

I added the iCloud Document Capabilities and I use the default Core Data Stack Template from Apple, when you create a new Swift-project with iOS 8.当您使用 iOS 8 创建新的 Swift 项目时,我添加了 iCloud 文档功能并使用 Apple 的默认核心数据堆栈模板。

Apple Core Data Stack Template:苹果核心数据栈模板:

    lazy var applicationDocumentsDirectory: NSURL = {
    let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
    return urls[urls.count-1] as! NSURL
}()

lazy var managedObjectModel: NSManagedObjectModel = {
    let modelURL = NSBundle.mainBundle().URLForResource("testapp", withExtension: "momd")!
    return NSManagedObjectModel(contentsOfURL: modelURL)!
}()

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
    var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("testapp.sqlite")
    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: url, options: nil, 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: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict as [NSObject : AnyObject])
        NSLog("Unresolved error \(error), \(error!.userInfo)")
        abort()
    }

    return coordinator
}()

lazy var managedObjectContext: NSManagedObjectContext? = {
    let coordinator = self.persistentStoreCoordinator
    if coordinator == nil {
        return nil
    }
    var managedObjectContext = NSManagedObjectContext()
    managedObjectContext.persistentStoreCoordinator = coordinator
    return managedObjectContext
}()

// MARK: - Core Data Saving support

func saveContext () {
    if let moc = self.managedObjectContext {
        var error: NSError? = nil
        if moc.hasChanges && !moc.save(&error) {
            NSLog("Unresolved error \(error), \(error!.userInfo)")
            abort()
        }
    }
}

I read at apple developer about the function migratePersistentStore and here at StackOverflow about the answer Move local Core Data to iCloud , but I don't know how to implement this correct in that template.我在苹果开发人员处阅读了有关 migratePersistentStore 函数的内容,并在 StackOverflow 上阅读了有关将本地核心数据移动到 iCloud的答案,但我不知道如何在该模板中正确实现这一点。

From my understanding, I think, I have to check in the lazy var definition of the coordinator, if the url points to an existing store/file.根据我的理解,我认为,如果 url 指向现有的存储/文件,我必须检查协调器的惰性 var 定义。 If so, I have to migrate to a new store with the function migratePersistentStore:xmlStore and option NSPersistentStoreUbiquitousContentNameKey.如果是这样,我必须使用函数 migratePersistentStore:xmlStore 和选项 NSPersistentStoreUbiquitousContentNameKey 迁移到新商店。

So I wrote a new persistence coordinator:所以我写了一个新的持久化协调器:

Persistence Coordinator with migration具有迁移功能的持久性协调器

   lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
    var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)

    // create new iCloud-ready-store
    let iCloudStoreUrl = self.applicationDocumentsDirectory.URLByAppendingPathComponent("TestAppiCloud.sqlite")
    var iCloudOptions: [NSObject : AnyObject]? = [
        NSPersistentStoreFileProtectionKey: NSFileProtectionComplete,
        NSMigratePersistentStoresAutomaticallyOption: true,
        NSInferMappingModelAutomaticallyOption: true,
        NSPersistentStoreUbiquitousContentNameKey: "TestAppiCloudStore"
    ]

    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: iCloudStoreUrl, options: iCloudOptions, error: &error) == nil {
        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)
        NSLog("Unresolved error \(error), \(error!.userInfo)")
        abort()
    }

    // Possible old store migration

    let existingStoreUrl = self.applicationDocumentsDirectory.URLByAppendingPathComponent("testapp.sqlite")
    let existingStorePath = existingStoreUrl.path

    // check if old store exists, then ...
    if NSFileManager.defaultManager().fileExistsAtPath(existingStorePath!) {
        // ... migrate
        var existingStoreOptions = [NSReadOnlyPersistentStoreOption: true]
        var migrationError: NSError? = nil

        var existingStore = coordinator!.persistentStoreForURL(existingStoreUrl)
        coordinator!.migratePersistentStore(existingStore!, toURL: iCloudStoreUrl, options: existingStoreOptions, withType: NSSQLiteStoreType, error: &migrationError)
    }

    // iCloud Notifications
    let notificationCenter = NSNotificationCenter.defaultCenter()
    notificationCenter.addObserver(self,
        selector: "storeWillChange",
        name: NSPersistentStoreCoordinatorStoresWillChangeNotification,
        object: coordinator!)
    notificationCenter.addObserver(self,
        selector: "storeDidChange",
        name: NSPersistentStoreCoordinatorStoresDidChangeNotification,
        object: coordinator!)
    notificationCenter.addObserver(self,
        selector: "storeDidImportUbiquitousContentChanges",
        name: NSPersistentStoreDidImportUbiquitousContentChangesNotification,
        object: coordinator!)

    return coordinator
    }()

But I'm getting an exception, when the migration starts at coordinator!.migratePersistentStore:但是当迁移从 coordinator!.migratePersistentStore 开始时,我遇到了一个异常:

var existingStore = coordinator!.persistentStoreForURL(existingStoreUrl) // nil var existingStore = coordinator!.persistentStoreForURL(existingStoreUrl) // nil

but the fileManager says, it exists!但文件管理器说,它存在!

What am I doing wrong?我究竟做错了什么? Is the idea correct?这个想法正确吗? Please help.请帮忙。

I ran into the exact same issue with "persistentStoreForURL".我遇到了与“persistentStoreForURL”完全相同的问题。 It turns out what I was looking for would have been something like "persistenStoreWITHUrl".事实证明,我一直在寻找类似“persistenStoreWITHUrl”的东西。 I assumed incorrectly that it would actually load the store but as it turns out, it does not.我错误地认为它实际上会加载商店,但事实证明,它不会。 This function works when the store is already loaded in the coordinator.当商店已经加载到协调器中时,此功能起作用。 I realize you asked this a while ago, but I'm going to leave this here in case anyone else runs into the same issue.我知道你刚才问过这个问题,但我会把这个留在这里,以防其他人遇到同样的问题。 Changing the code thusly will get it to work as intended:因此更改代码将使其按预期工作:

    let coordinator = self.persistentStoreCoordinator
    let existingStore = coordinator.persistentStores.first

    var options = Dictionary<NSObject, AnyObject>()
    options[NSPersistentStoreRemoveUbiquitousMetadataOption] = true
    options[NSMigratePersistentStoresAutomaticallyOption] = true
    options[NSInferMappingModelAutomaticallyOption] = true


    do {
         try coordinator?.migratePersistentStore(existingStore!, to: url2, options: [NSMigratePersistentStoresAutomaticallyOption:true, NSInferMappingModelAutomaticallyOption:true], withType: NSSQLiteStoreType)

    }
    catch {

        print(error)

    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM