MagicalRecord: multiple databases
Asked Answered
A

3

10

I have an app that uses MagicalRecord, and I'm pre-populating the database with a large amount of data that is used for reference. Within that same data model, I have user-definable information pertaining to things the user may do in the app.

The app was rejected because the pre-populated data is supposed to be marked as "do not backup". So, I'd like to have that data in a separate datastore so that I can keep the user data as backupable.

Is there a way to have two separate datastores using MagicalRecord?

Arnst answered 20/6, 2012 at 16:21 Comment(0)
L
15

I think it's possible, but not too easy though. As you know, to work with more than one database, you should bring some changes to your PersistentStoreCoordinator, so it will have two PersistentStores. After this, you Core Data stack would look like this: enter image description here

The other way is two make two separate PersistentStoreCoordinators, each carrying one store.

In Magical Record, there are several class methods for adding stores in NSPersistentStoreCoordinator+MagicalRecord.h.

  • (NSPersistentStore *) MR_addInMemoryStore;
  • (NSPersistentStore *) MR_addAutoMigratingSqliteStoreNamed:(NSString *) storeFileName;
  • (NSPersistentStore *) MR_addSqliteStoreNamed:(id)storeFileName withOptions:(__autoreleasing NSDictionary *)options;

I think, that this is the place where you could do the thing you want.

Also i should mention, that the whole proccess of setting up the stack goes in MagicalRecord+Setup.h

+ (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName

So you can add your Stores and Coordinators there. I've never managed it by myself, that was just a brief investigation of a possible solution.

Lubric answered 20/6, 2012 at 19:11 Comment(0)
A
6

I was able to solve this issue using configurations. Since Magical Record always sends null for the configuration parameter, I broke apart setupCoreDataStackWithAutoMigratingSqliteStoreNamed and replaced it with a method that supports multiple configurations.

Because Magical Record does a good job of handling auto migrations, I first call setupCoreDataStackWithAutoMigratingSqliteStoreNamed, followed by cleanup, and then I supply my replacement code.

I have one object model with my seed data objects assigned the the "Seed" configuration and user objects assigned to the "User" configuration. Magical Record has already been initialized so it could auto migrate if necessary.

+(void) RB_setupMultipleStores:(NSString *) seedStoreName userStore:(NSString *) userStoreName
/* change persistent store to one with multiple configurations. Assumes Magical Record is initialized. */
{
NSError * error= nil;

[MagicalRecord cleanUp];

NSManagedObjectModel * model = [NSManagedObjectModel MR_defaultManagedObjectModel];

NSURL *seedURL = [NSPersistentStore MR_urlForStoreName:[seedStoreName stringByAppendingString:@".sqlite"]];

NSPersistentStoreCoordinator * coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                         [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                         [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
                         nil];

NSPersistentStore * seedStore =[coordinator
                                  addPersistentStoreWithType:NSSQLiteStoreType
                                  configuration:@"Seed"
                                  URL:seedURL
                                  options:options
                                  error:&error];
if (!seedStore || error)
{
    NSLog(@"Error setting up seed store:%@ for %@", [error localizedDescription], seedURL);
    exit(-1);
}

NSURL *userURL = [NSPersistentStore MR_urlForStoreName:[userStoreName stringByAppendingString:@".sqlite"]];

NSPersistentStore * userStore = [coordinator
                                 addPersistentStoreWithType:NSSQLiteStoreType
                                 configuration:@"User"
                                 URL:userURL
                                 options:options
                                 error:&error];

if (!userStore || error)
{
    NSLog(@"Error setting up user store:%@ for %@", [error localizedDescription], userURL);
    exit (-1);
}
[NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:coordinator];

[NSManagedObjectContext MR_initializeDefaultContextWithCoordinator:coordinator];
}

Also, MR 3.0 has concurrent stacks which may solve the problem once it is done.

Aluino answered 3/6, 2014 at 19:4 Comment(1)
Hi @Ron, which store do you pass to setupCoreDataStackWithAutoMigratingSqliteStoreNamed as you have two of them? The one with no seed?Too
P
2

Keeping data for different Core Data entities in different store files is well supported and fairly straightforward. However, MagicalRecrd doesn't provide any convenience methods for setting up your Core Data stack in this way. You simply have to allocate your stack manually, and tell MagicalRecord to use the NSPersistentStoreCoordinator you create. Here's how I did it in swift:

import Foundation
import CoreData
import MagicalRecord

class CoreDataSetup {
    static func setupAutoMigratingStack(withContentConfigurationName contentConfigurationName: String, userConfirgurationNameName: String) {
        MagicalRecord.cleanUp()

        let managedObjectModel = NSManagedObjectModel.MR_defaultManagedObjectModel()
        let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel!)

        let contentURL = NSPersistentStore.MR_urlForStoreName(contentConfigurationName + ".sqlite")
        let userURL = NSPersistentStore.MR_urlForStoreName(userConfirgurationNameName + ".sqlite")
        let options = [
            NSMigratePersistentStoresAutomaticallyOption : true,
            NSInferMappingModelAutomaticallyOption: true,
            NSSQLitePragmasOption: ["journal_mode": "DELETE"]
        ]
        do {
            try persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: contentConfigurationName, URL: contentURL, options: options)
            try persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: userConfirgurationNameName, URL: userURL, options: options)

            NSPersistentStoreCoordinator.MR_setDefaultStoreCoordinator(persistentStoreCoordinator)
            NSManagedObjectContext.MR_initializeDefaultContextWithCoordinator(persistentStoreCoordinator)
        } catch {
            print("Error adding persistent store to coordinator: \(error) ")
        }
    }
}

Note that in my code I'm referring to your concept of the "seed" store as "content" and the user-definable store as "user".

To accomplish the second aspect of your question, configuring the content store to not be backed up, you simply have to play around with the URLs where you store each store, placing the content store in a non-backed up temporary directory, and copying it to that location up launch from your app bundle if it doesn't exist.

Prognosis answered 10/3, 2016 at 20:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.