Question

I am currently updating an app to use Core Data. The app you could say is a "database viewer", only one database is able to be viewed at a time. Each database is kept in its own separate folder. Currently the data is downloaded and stored as a set of plist files.

In the new version I need to convert these plist databases into Core Data stores (one store for each database.) I've already setup the methods that create the separate store files, and crete the entities. The problem is that all the entities are saved to the first database I created, not to the "current" or "lastly created" file.

The basic process I'm using is:

//For each database {
//Create the sqlite file and set up NSManagedObjectContext
[MagicalRecord setupCoreDataStackWithStoreNamed:
    [NSURL fileURLWithPath:
    [NSString stringWithFormat:@"%@/%@/%@.sqlite",
    dirPath, directory, directory]]];
NSManagedObjectContext *managedObjectContext = 
    [NSManagedObjectContext MR_contextForCurrentThread];

//Iterate through all the plist files and create the necessary entities.
//Save new entities to file
[managedObjectContext MR_save];
//Clean up all cashes
[MagicalRecord cleanUp];
}

How would one properly switch between stores, essentially "reseting" everything between each switch. Preferably (if possible) using magical record.

EDIT: I've found out a portion of the problem, and removed most of the unwanted behavior. It turns out, you can't reliably call [MagicalRecord cleanUp] on a background thread. Also, It isn't doing what I think it should (see below). I ended up calling back to the main thread after each "save" to reset the Core Data stack. Doing this creates a new context for the first three databases. after that, it duplicates the context from the database three databases ago. So the same three contexts are used in a loop.

This is what I currently have; I start the process by creating a background thread and run the code to create a single database in the background:

backgroundQueue = dispatch_queue_create("com.BrandonMcQuilkin.myQueue", NULL);
    dispatch_async(backgroundQueue, ^(void) {
        [self createSQLiteDatabase:updateList];
    });

Then creating the stack and database:

- (void)createSQLiteDatabase:(NSArray *)updateList
{
    NSString *directory = [updateList objectAtIndex:0];
    [MagicalRecord setupCoreDataStackWithStoreNamed:
        [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@/%@.sqlite",
        dirPath, directory, directory]]];
    NSManagedObjectContext *managedObjectContext = 
        [NSManagedObjectContext MR_contextForCurrentThread];
    //Check to see if the stack has reset
    NSLog(@"Before:%i", [[Competition MR_findAllInContext:managedObjectContext] count]);

    //Create and add entities to context...

    //Prepare for next loop
    NSLog(@"After:%i", [[Competition MR_findAllInContext:managedObjectContext] count]);
    [managedObjectContext MR_saveNestedContexts];
    [NSManagedObjectContext MR_resetContextForCurrentThread];

    NSMutableArray *temp = [[NSMutableArray alloc] initWithArray:updateList];
    [temp removeObjectAtIndex:0];

    dispatch_async(dispatch_get_main_queue(), ^(void){
        [self shouldContinueUpdating:temp];
    });

Then reset everything and repeat for all databases:

- (void)shouldContinueUpdating:(NSArray *)databases
{
    //preform cleanup on main thread and release background thread
    [MagicalRecord cleanUp];
    dispatch_release(backgroundQueue);

    if ([databases count] != 0) {
        backgroundQueue = dispatch_queue_create("com.BrandonMcQuilkin.myQueue", NULL);
        dispatch_async(backgroundQueue, ^(void) {
            [self createSQLiteDatabase:databases];
        });
    }
}

With the two NSLogs, I get this in the console: (using six databases, the pattern is the same no matter how many databases I convert.)

//First Loop
Before:0
After:308
//Second Loop
Before:0
After:257
//Third Loop
Before:0
After:37
//Fourth Loop
Before:308 
After:541 
//Fifth Loop
Before:257
After:490
//Sixth Loop
Before:37
After:270
... Keep adding to each of the three contexts.

And [MagicalRecord cleanUp] isn't doing what It say it's doing. Here is what the method is supposed to do.

+ (void) cleanUpStack;
{
[NSManagedObjectContext MR_cleanUp];
[NSManagedObjectModel MR_setDefaultManagedObjectModel:nil];
[NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:nil];
[NSPersistentStore MR_setDefaultPersistentStore:nil];
}

But It turns out that the NSStoreCoordinator every time I save, is the same coordinator, in the same memory location, and each store is hanging around. Something is not working right...

Was it helpful?

Solution 2

Turns out The problem I'm having is because of a bug in MagicalRecord. I've submitted a git issue here: https://github.com/magicalpanda/MagicalRecord/issues/270

OTHER TIPS

MagicalRecord may not be the best tool for this job for you...

First, let's correct your usage of the setupCoreDataStackWithStoreNamed: method. The parameter takes an NSString, not a URL, nor a file path. MagicalRecord will pick the proper path for you and create your store there. your resulting sqlite file is likely to be named with the path you intended it to be.

Next thing, you'll need to dynamically create your CoreData model for this file. This is kind of tough, but possible. You'll need to traverse these plist files, and interpret entities, attributes and relationships, and create corresponding NSEntityDescriptions, NSAttributeDescriptions and NSRelationshipDesctiptions and populate an NSManagedObjectModel "manually". Youll want to look for the method

- [NSManagedObjectModel setEntities:(NSArray *)]

as well as the creation methods for NSEntityDescription, NSAttributeDescription and NSRelationshipDescription.

You'll also want to save this model somewhere so you don't have to recreate it every time. Luckily, it conforms to NSCoding, so you should just be able to save it to disk.

After that, you'll probably want to populate your data. From here, MagicalRecord can help you. I suggest looking at the Importing Data Made Easy blog post I wrote for Cocoa is My Girlfriend

If you want to "switch stores", which I guess means you want to create a new store for each plist file you've got, then you're going to have to tear down the entire Core Data stack for each file. If you manage to use MagicalRecord for this project, you'll need to look at [MagicalRecord cleanUp], and start over. If each model was the same, you could get by with releasing your Persistent Store Coordinator, and creating a new one to your store. But since your "schemas" will probably be different, you'll just want to scratch everything and start over.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top