iOS - Managing two CoreData models with MagicalRecord

你说的曾经没有我的故事 提交于 2020-01-06 08:39:21

问题


I'm using MagicalRecord to work with a CoreData model, which is likely to be versioned in the future.

Now I need to add to my app a pre-populated database with one entity of about 80000 objects; this data is static and I'm not expected it will ever change.

If I added this entity to the existing model I would need to generate a new seed db every time the model changes, increasing the project complexity.

A better solution would be creating a second model, just for the new entity: the seed db will never change and the first model could take care of its versioning regardless of the new model. No relation between the two models is required.

On top of the existing model I'm using RestKit too and here's how everything is setup:

[MagicalRecord setupAutoMigratingCoreDataStack];
RKManagedObjectStore *managedObjectStore =
    [[RKManagedObjectStore alloc] initWithPersistentStoreCoordinator:
        [NSPersistentStoreCoordinator MR_newPersistentStoreCoordinator]];
self.objectManager.managedObjectStore = managedObjectStore;
[managedObjectStore createManagedObjectContexts];
// bind RK with MagicalRecord
[NSManagedObjectContext MR_setRootSavingContext:
    managedObjectStore.persistentStoreManagedObjectContext];
[NSManagedObjectContext MR_setDefaultContext:
    managedObjectStore.mainQueueManagedObjectContext];
managedObjectStore.managedObjectCache = [[RKFetchRequestManagedObjectCache alloc] init];

The new model will not be used with RestKit.

Is this feasible with MagicalRecord? I've been through its documentation but could find anything useful.

Many thanks, DAN

UPDATE

Let's have a db model with 4 entities (Foo, Bar, Blarg, Baz) created with the xcode editor. The model editor has a Default configuration which cannot be removed, so we can only add two new configurations (SeedConfiguration and UserConfiguration), add Foo to the first and the other three to the second. The two configurations should be saved in seed.sqlite and user.sqlite. At this point I'd like to run a script which populates seed.sqlite with thousands of Foo objects: once generated this file will be put in the project resources and copied in the app directory at startup; user.sqlite will be instead generated at runtime and used to manage the user info.

When I launch the app in "script" mode to populate seed.sqlite, the two sqlite files are correctly created but both of them contain all the entities, whereas I would expect to find Foo in seed.sqlite and Bar, Blarg, Baz in user.sqlite.

Should I insert the Foo objects and copy the resulting seed.sqlite even if it contains all the other (empty) entities?

Here's how two persistence stores in one single coordinator can be created : https://stackoverflow.com/a/24022978/2515181

For the sake of clarification if I could just have one single sqlite file it would be great, but doing so I'd have to generate the seed db every time the model changes.


回答1:


I didn't want to get into a long answer because I do not use MagicalRecord, and I have NO IDEA how it manages model configurations.

That being said, the way you want to approach this problem is by using model configurations and multiple store files. The problem is both well understood and well documented.

Apple's documentation is a good starting point, and there are numerous articles and examples on the web.

EDIT

OK DAN, here is a somewhat contrived (but simple) example for using multiple configurations. You should be able to copy/paste this into a test file and run it, which should allow you to trace what's going on and get a basic understanding.

Note, that this is not the way I would advise writing production code nor tests (I also don't advise ignoring errors), but I hope this helps explain some things a bit and allows you to experiment.

I broke the code into several helper methods to hopefully better explain.

First, let's create a simple model, with four entities, where we will put two into each configuration.

- (NSManagedObjectModel *)makeConfigurationModel {
    NSAttributeDescription *nameAttr = [[NSAttributeDescription alloc] init];
    nameAttr.name = @"name";
    nameAttr.attributeType = NSStringAttributeType;

    NSEntityDescription *foo = [[NSEntityDescription alloc] init];
    foo.name = @"Foo";
    foo.properties = @[[nameAttr copy]];
    NSEntityDescription *bar = [[NSEntityDescription alloc] init];
    bar.name = @"Bar";
    bar.properties = @[[nameAttr copy]];

    NSEntityDescription *blarg = [[NSEntityDescription alloc] init];
    blarg.name = @"Blarg";
    blarg.properties = @[[nameAttr copy]];
    NSEntityDescription *baz = [[NSEntityDescription alloc] init];
    baz.name = @"Baz";
    baz.properties = @[[nameAttr copy]];


    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
    model.entities = @[foo, bar, blarg, baz];
    [model setEntities:@[foo, bar] forConfiguration:@"One"];
    [model setEntities:@[blarg, baz] forConfiguration:@"Two"];

    return model;
}

Next, a function to assign both stores to a PSC, and create some example entities. This function also checks to make sure all entities can be accessed.

- (void)setupDatabaseWithModel:(NSManagedObjectModel*)model
                        store1:(NSURL*)store1URL
                        store2:(NSURL*)store2URL {
    @autoreleasepool {
        NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]
            initWithManagedObjectModel:model];
        [psc addPersistentStoreWithType:NSSQLiteStoreType
                          configuration:@"One"
                                    URL:store1URL
                                options:nil
                                  error:NULL];
        [psc addPersistentStoreWithType:NSSQLiteStoreType
                          configuration:@"Two"
                                    URL:store2URL
                                options:nil
                                  error:NULL];
        NSManagedObjectContext *moc = [[NSManagedObjectContext alloc]
            initWithConcurrencyType:NSMainQueueConcurrencyType];
        moc.persistentStoreCoordinator = psc;

        // Add some entities...
        NSArray *entityNames = @[@"Foo", @"Bar", @"Blarg", @"Baz"];
        for (NSString *e in entityNames) {
            NSManagedObject *obj =
                [NSEntityDescription insertNewObjectForEntityForName:e
                                              inManagedObjectContext:moc];
            [obj setValue:[NSString stringWithFormat:@"%@ 1", e] forKey:@"name"];
        }
        [moc save:NULL];

        // Should have all of them in this MOC...
        for (NSString *e in entityNames) {
            NSFetchRequest *fetchRequest = [NSFetchRequest
                fetchRequestWithEntityName:e];
            NSArray *result = [moc executeFetchRequest:fetchRequest error:NULL];
            XCTAssertEqual(1, result.count);
            NSManagedObject *obj = [result firstObject];
            XCTAssertEqualObjects(([NSString stringWithFormat:@"%@ 1", e]),
                                  [obj valueForKey:@"name"]);
        }
    }
}

And a function to check that certain entities are (or are not) in the store.

- (void)checkStore:(NSURL*)storeURL
             model:(NSManagedObjectModel*)model
           present:(NSArray*)present
        notPresent:(NSArray*)notPresent {
    @autoreleasepool {
        NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]
            initWithManagedObjectModel:model];
        [psc addPersistentStoreWithType:NSSQLiteStoreType
                          configuration:nil
                                    URL:storeURL
                                options:nil
                                  error:NULL];
        NSManagedObjectContext *moc = [[NSManagedObjectContext alloc]
            initWithConcurrencyType:NSMainQueueConcurrencyType];
        moc.persistentStoreCoordinator = psc;

        for (NSString *e in present) {
            NSFetchRequest *fetchRequest = [NSFetchRequest
                fetchRequestWithEntityName:e];
            NSArray *result = [moc executeFetchRequest:fetchRequest error:NULL];
            XCTAssertEqual(1, result.count);
            NSManagedObject *obj = [result firstObject];
            XCTAssertEqualObjects(([NSString stringWithFormat:@"%@ 1", e]),
                                  [obj valueForKey:@"name"]);
        }
        for (NSString *e in notPresent) {
            NSFetchRequest *fetchRequest = [NSFetchRequest
                fetchRequestWithEntityName:e];
            NSArray *result = [moc executeFetchRequest:fetchRequest error:NULL];
            XCTAssertEqual(0, result.count);
        }
    }
}

And a little helper to remove the URL

static void removeURL(NSURL ** url) {
    [[NSFileManager defaultManager] removeItemAtURL:*url error:NULL];
}

And a test function...

- (void)testConfigurations {
    __attribute__((cleanup(removeURL))) NSURL * __autoreleasing dirURL =
        [[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory
                                                inDomain:NSUserDomainMask
                                       appropriateForURL:nil
                                                  create:YES
                                                    error:NULL]
            URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
    [[NSFileManager defaultManager] createDirectoryAtURL:dirURL
                             withIntermediateDirectories:YES
                                              attributes:nil
                                                   error:NULL];

    NSManagedObjectModel *model = [self makeConfigurationModel];
    NSURL *store1URL = [dirURL URLByAppendingPathComponent:@"store1"];
    NSURL *store2URL = [dirURL URLByAppendingPathComponent:@"store2"];
    [self setupDatabaseWithModel:model store1:store1URL store2:store2URL];
    [self checkStore:store1URL
               model:model
             present:@[@"Foo", @"Bar"]
          notPresent:@[@"Blarg", @"Baz"]];
    [self checkStore:store2URL
               model:model
             present:@[@"Blarg", @"Baz"]
          notPresent:@[@"Foo", @"Bar"]];
}


来源:https://stackoverflow.com/questions/33165653/ios-managing-two-coredata-models-with-magicalrecord

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!