I want to enable iCloud for Core Data for an existing project. The user could use the app from a new iPhone or maybe another with his old data. So I have to do a migration of the probably existing store to the new store in iCloud / ubiquitous container.
我想为现有项目启用iCloud的核心数据。用户可以使用新iPhone上的应用程序,也可以使用旧数据上的应用程序。因此,我必须将现有的商店迁移到iCloud / ubiquitous容器中的新商店。
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.
我添加了iCloud文档功能,并使用苹果的默认核心数据堆栈模板,当您使用iOS 8创建一个新的斯威夫特项目时。
Apple Core Data Stack Template:
Apple Core Data Stack模板:
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 * about the answer Move local Core Data to iCloud, but I don't know how to implement this correct in that template.
我在苹果的开发者那里读到关于这个函数的迁移,在*中,关于这个问题的答案将本地核心数据转移到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. If so, I have to migrate to a new store with the function migratePersistentStore:xmlStore and option NSPersistentStoreUbiquitousContentNameKey.
根据我的理解,我认为,如果url指向一个现有的存储/文件,我必须检查协调器的惰性var定义。如果是这样,我必须迁移到具有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:
但是我有一个例外,当迁移从协调器开始时!
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.
我做错了什么?这个想法正确吗?请帮助。
1 个解决方案
#1
1
I ran into the exact same issue with "persistentStoreForURL". It turns out what I was looking for would have been something like "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:
我遇到了与“persistentStoreForURL”完全相同的问题。我要找的是类似于"persistenStoreWITHUrl"的东西。我错误地认为它会实际加载存储,但事实证明,它不会。当存储已经加载到协调器中时,此函数将正常工作。我知道你刚才问过这个问题,但我要把它留在这里,以防其他人遇到同样的问题。更改代码以使其正常工作:
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)
}
#1
1
I ran into the exact same issue with "persistentStoreForURL". It turns out what I was looking for would have been something like "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:
我遇到了与“persistentStoreForURL”完全相同的问题。我要找的是类似于"persistenStoreWITHUrl"的东西。我错误地认为它会实际加载存储,但事实证明,它不会。当存储已经加载到协调器中时,此函数将正常工作。我知道你刚才问过这个问题,但我要把它留在这里,以防其他人遇到同样的问题。更改代码以使其正常工作:
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)
}