Вопрос

I have inherited an iOS app that needs to undergo major, major changes. In its current form, it uses a single UIManagedDocument (not synced with iCloud, but just used to avoid Core Data boilerplate) to store user data. I need to make drastic changes to the data model and I'd like to switch to a normal Core Data stack. The existing codebase was also pretty unusable, so I decided to create a new project but with the same app ID so it can go out as an update.

I'm not sure how to import existing users' data to the new data model.

The current model has 3 main entities and I only need 2 attributes from each of them (the rest of the old data can get thrown away). I've created my new data model, and then I copied all the old model files from previous project plus the old data model.

Then I wrote an importer class:

-(BOOL)isImportNeeded
{
    return [[NSFileManager defaultManager] fileExistsAtPath:[[self URLToOldModel] path]];
}

-(NSURL*)URLToOldModel
{
    NSURL* URL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    return [URL URLByAppendingPathComponent:@"Data"];
}

-(UIManagedDocument*)managedDocumentAtPath:(NSString*)path
{
    return [[UIManagedDocument alloc] initWithFileURL:[self URLToOldModel]];
}

-(void)performWithDocument:(void (^)(void))onDocumentReady
{
    if (self.managedDoc.documentState == UIDocumentStateClosed) {
        // I put this code here for debug purposes
        NSError* e = nil;
        if (![self.managedDoc readFromURL:self.managedDoc.fileURL error:&e]) {
            NSLog(@"Couldn't read UIManagedDocument: %@", [e localizedDescription]);
        }
//        [self.managedDoc openWithCompletionHandler:^(BOOL success){
//            if (success)
//                onDocumentReady();
//            else
//                NSLog(@"Could not open document");
//        }];
    }
    else if (self.managedDoc.documentState == UIDocumentStateNormal)
    {
        onDocumentReady();
    }
}

-(void)import
{
    self.managedDoc = [self managedDocumentAtPath:[[self URLToOldModel] path]];
    self.sourceManagedObjectContext = self.managedDoc.managedObjectContext;

    [self performWithDocument:^{ ... }];
}

That NSLog prints out the following: Couldn't read UIManagedDocument: The operation couldn’t be completed. (Cocoa error 134100.) Drilling a bit more into the error's userInfo dictionary, I get this as the error reason: "The model used to open the store is incompatible with the one used to create the store".

I've made sure that the old data model is added to the project and is in the "Compile Sources" build phase.

I learned more about UIManagedDocument and realized that when it's getting initialized, it automatically creates a union of all data models in bundle and the way to stop this is by subclassing UIManagedDocument and overriding the managedObjectModel accessor. I did so, by creating a subclass with just the following method:

-(NSManagedObjectModel*)managedObjectModel
{
    NSString* modelPath = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"momd"];
    modelPath = [modelPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL* modelURL = [NSURL URLWithString:modelPath];
    return [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
}

However, I still get the same error.

Это было полезно?

Решение

A friend of mine suggested this, which I've used in every core data app I've ever worked on - no idea how it escaped me:

self.managedDoc.persistentStoreOptions = @{NSIgnorePersistentStoreVersioningOption: @YES,
                                           NSMigratePersistentStoresAutomaticallyOption: @YES,
                                           NSInferMappingModelAutomaticallyOption: @YES};

That did it.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top