Search code examples
objective-ccore-datarealmcore-data-migrationdata-integrity

Steps to take in migrating data from Core Data to Realm?


I was reading this article by Tim Oliver (Migrating an App from Core Data to Realm) and I came across the following paragraph, which has been the most guidance I have been able to find as far as converting my app from Core Data to Realm:

"Once you’ve migrated to Realm, you can re-link the Core Data framework back into your app, use raw NSManagedObject objects to fetch your users’ data from Core Data, and then manually pass it over to Realm. You can leave this migration code in your app permanently, or simply remove it after a sufficient period of time has passed."

This seems like a good idea; however, I have not been able to find even one example of how this should be done.

I have already changed all of my Core Data classes over to Realm, so they cannot be used any more. I have the xcdatamodel file available to the app and the core data framework is easily turned back on. Do I need to set up the whole persistent store controller, managedObjectContext, etc. all over again?

While undesirable, that can be done if necessary. Then I am left to figure out, given that 'raw' NSManagedObject will need to be used, how the many-to-many and one-to-many relationships and their inverses will be able to be properly captured.

Can anyone point me in the right direction on this? I am using objective-c but would benefit from examples in Swift if there are any to be pointed to.


Solution

  • I'm the Tim Oliver who wrote that article. :)

    So in my own app (for which that article was based on), when I moved from Core Data to Realm, I did a clean break. All of the data I was storing was just cached metadata derived by the document files in the app, so I opted to simply delete the Core Data SQLite file on disk, and then it was only going to be a mild inconvenience the next time the user opened the app as the metadata was re-calculated and stored in Realm.

    If you've got data in your Core Data file, then the onerous is on you to perform a one-off migration from Core Data to Realm. It's not recommended to access the raw data from the SQLite file directly, so the only way is to keep your Core Data code around and use it to query and copy the data.

    What I meant by 'raw NSManagedObjects' is that since Core Data objects are KVC-compliant, if you've already refactored your model classes into Realm classes, you can access the data straight from the base NSManagedObject class using the KVC methods like -[NSObject valueForKey:].

    I briefly tested this in one of the official Apple Core Data sample code projects to confirm it was still working.

    Originally, accessing data from a Core Data Book object looked like this:

    - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    
        // Configure the cell to show the book's title
        Book *book = [self.fetchedResultsController objectAtIndexPath:indexPath];
        cell.textLabel.text = book.title;
    }
    

    But once you've refactored your Book class into RLMObject, you can still access data from your Core Data store like this:

    - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    
        // Configure the cell to show the book's title
        NSManagedObject *book = [self.fetchedResultsController objectAtIndexPath:indexPath];
        cell.textLabel.text = [book valueForKey:@"title"];
    }
    

    In this way, you should still be able to access the data in your Core Data objects like before, but you're free to use the actual classes for Realm now.

    Unfortunately, like you mentioned in the comments, this does mean you need to keep some amount of Core Data code around in order to be able to open the Core Data store and get the data out of it.

    But ideally, you should be able to confine all of this code to a single 'migrator' class that only needs to run the first time it detects that the user's data hasn't been copied to Realm yet. And after a sufficient amount of time has passed, you could then consider dropping it completely.