Y.Bonafons
Y.Bonafons

Reputation: 2349

Save part of Core Data into the cloud

Working on iOS 7, I have to debug an application using Core Data with several entities. First I was asking for adding iCloud (and not cloud kit) to save all the data. But then, the client realized he wanted to save only some entities but not all of them into the cloud.

Is it something possible ? Do I need to use several NSPersistentStoreCoordinator ? (the application already use several NSManagedObjectContext, one per entity). Or maybe I can do something when I receive the notification :

NSPersistentStoreDidImportUbiquitousContentChangesNotification

and manually perform the merge but I really don't know how.

Thanks for your help.


Thanks to Tom Harrington, I created 2 configurations: CloudConfiguration and LocalConfiguration and I add some entities in each (this link helps me too).

Then, I add persistent store in the coordinator:

    // Configure persistentStoreCoordinator
    NSError* error1 = nil;
    NSString *cloudConfiguration = @"CloudConfiguration";
    NSPersistentStore *store1 = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                                         configuration:@"CloudConfiguration"
                                                                                   URL:[self storeURLForConfiguration:cloudConfiguration]
                                                                               options:@{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" }
                                                                                 error:&error1];
    if (error1) {
        NSLog(@"Error: %@ \n Description : %@ \nUser info : %@", error1, error1.description, error1.userInfo);
    }
    NSLog(@"*************** cloud store url ************** : %@", store1.URL);

    NSError* error2 = nil;
    NSString *localConfiguration = @"LocalConfiguration";
    NSPersistentStore *localStore = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                      configuration:localConfiguration
                                                                URL:[self storeURLForConfiguration:localConfiguration]
                                                            options:nil
                                                              error:&error2];
    if (error2) {
        NSLog(@"Error: %@ \n Description : %@ \nUser info : %@", error2, error2.description, error2.userInfo);
    }

    NSLog(@"*************** local store url ************** : %@", localStore.URL);

All of my entities goes in one store only (cloud or local). Entities in different store don't have any relationship and I have a single model.

So here I go, I start my app for the first time all seems to be well configured. But when I try on another device or on the same device after deleting the app, I have a crash, just after getting : Using local storage: 0.

Here is the crash log :

[_PFUbiquityRecordImportOperation main](734): CoreData: Ubiquity:  Error importing transaction log: <PFUbiquityTransactionLog: 0x16ed7f50>
transactionLogLocation: <PFUbiquityLocation: 0x16ed7ee0>: /var/mobile/Library/Mobile Documents/6ULEJ9RYTQ~fr~company~iCloudTestApp/CoreData/iCloudStore/mobile~E722813A-96E8-4E11-8DDE-56FF3837DEBD/iCloudStore/EU31J4aJIvvEyVMcWWYs1qgVajMk4_4fQxw1oe_Q0i0=/4C6B58B3-6C8D-4393-9B1E-8E48C7352091.1.cdt
transactionNumber: 1, exception: Invalid parameter value (bad entity)
User Info: (null)
2014-12-02 12:35:34.837 iCloudTestApp[1421:3b0b] -[_PFUbiquityRecordsImporter discoverAndImportAllAvailableLogs:error:](727): CoreData: Ubiquity:  Exception while scanning for logs to import: Invalid parameter value (bad entity)
userInfo: (null)

It sounds weird to me because it's happen before the merge. Of course, I remove all the datas in the cloud before testing with those two configurations. If you have any idea...

Upvotes: 2

Views: 254

Answers (1)

Tom Harrington
Tom Harrington

Reputation: 70976

Not multiple persistent store coordinators, but multiple persistent store files. You can add multiple persistent stores to the same coordinator by calling addPersistentStoreWithType:configuration:URL:options:error: multiple times. You don't have to use the same options every time, so you can use iCloud options for one store file but leave them out for a different one.

But you need to be aware of a couple of things:

  1. You can't create relationships between objects in different persistent store files. If that's a problem, look into fetched properties. They're properties that transparently fetch objects using a predicate that you provide. They work sort of like one-way relationships.

  2. You need some way to tell Core Data which store file to use for new objects. There are a couple of ways to do this:

    • If some of your entities could go in either persistent store, you'll need to use [NSManagedObjectContext assignObject:toPersistentStore:] every time you create a new instance.
    • For entities that will always be in the same store file, look into "configurations" for your model file. This lets you create named subsets of your model that contain only some of the entities. Use the configuration name when you add the persistent store file. Then, all new instances of those entities will automatically go to the right file.

Additional, based on updated question:

If you already have iCloud data that you need to use, you can't just switch to using configurations. The existing iCloud transaction logs may contain references to entities that are not in your new iCloud-only configuration. When it tries to import that data, it will fail, and you'll get errors like the one you're seeing.

If this app is still in development, I'd say just delete all existing iCloud data and go with the configuration. If you need to keep the existing iCloud data, you must ensure that all entities in the current iCloud data are still available. That most likely means you'll have to do without configurations and instead assign objects to one store or the other in your code.

Upvotes: 4

Related Questions