Can multiple (two) persistent stores be used with one object model, while maintaining relations from one to the other?
Asked Answered
B

3

42

Introduction

My iOS project ships with a Core Data persistent store weighing some 160MB in SQLite format. There is a ton of grouped information in there, in which users should be able to mark favorites. For this, I need (at least part of) the database to have write capabilities. But of course persistent stores that ship in the application bundle are by design read-only.

If you want the store to have read-write capabilities, you should copy it to, e.g. the app's documents folder. I don't want to do this, because then the app would be twice the size, while the main part of that database is read-only anyway. That would be a waste of resources.

Multiple persistent stores for NSPersistentStoreCoordinator

This is why I thought of using two persistent stores. The first would be the big one in the bundle, and the second could be a small one in the documents folder, storing special "favorite" entities with relationships to the big store.

I know something is possible in this regard, but I can't find the specifics. Should one only use multiple stores if you also have multiple object models? Can one object model be 'distributed' over two persistent stores? When browsing through the Core Data Programming docs, I can't find any real reference about how to set this up. Also Marcus Zarra's book doesn't seem to delve into this topic:

It is possible to add more than one NSPersistentStore to the NSPersistentStoreCoordinator, which can be useful when dealing with data that is split into multiple files. However, in our exam- ple, we have a single file. (Marcus Zarra: "Core Data - Apple's API for Persisting Data on Mac OS X" page 71)

The Question

Who could tell me if what I'm thinking of is possible with Core Data and multiple persistent stores? And could you maybe provide a hint about how to achieve this? Online/offline resources that deal with the topic are very much appreciated too.

Bimetallic answered 8/3, 2011 at 11:24 Comment(3)
I nearly figured it out, see #10952344 sample projectSaavedra
You're overcomplicating it. Copy the db from the bundle to the library folder and be done with it.Slop
Considering you're either commenting on a 2-year-old question or a one-year-old comment on that question, you're not being very constructive here, @Rog. The app's database is now close to 250 Megs, of which I think its users appreciate it only being there once.Bimetallic
B
48

The answer is yes. @Caleb points to the right resources, but getting it to work is still quite awkward. I thought I'd place a resumé here:

For two NSPersistentStore instances to share the same model, you have to add a configuration to your model, which is a string-named subset of the entities:

Model configurations

In the model, to an entity that belongs to the second store, you add a fetched property (NSFetchedPropertyDescription for googlability). This is somewhat of a very simple stored procedure, and it could look like this:

NSPredicate format for the fetched property

Then, when you add the stores to your persistent store coordinator, you use the strings for the configuration argument (more info about the options here):

[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
                                          configuration:@"ModifyInBackground" 
                                                    URL:storeURL1
                                                options:options
                                                  error:&error]

[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
                                          configuration:@"ModifyInMain"
                                                    URL:storeURL2 
                                                options:options 
                                                  error:&error]

Finally, when you want to get from the entity in store B to the entity in store A, you trigger the fetched property like you would trigger a fault, just by accessing it.

Note: A fetched property always returns an NSArray, because the predicate you write to establish the link might have multiple results. If you want to get to just one entity, you could place something like this in a wrapper method of your NSManagedObject subclass:

Wallpaper *recordedWallpaper = [record.wallpaper lastObject];
Bimetallic answered 17/11, 2012 at 23:26 Comment(4)
You are so awesome, a piece of code and 2 pictures tell much more than Apple's document, thank you!Dory
It's StackOverflow that deserves the real credit, but you made my day nonetheless, thanks!Bimetallic
Quick question: in the addPersistentStoreWithType:... code you posted, what is options? I built my app from one of the templates, and it just sends nil to options:. EDIT: found itInsipience
See developer.apple.com/LIBRARY/IOS/documentation/Cocoa/Reference/… Something like: NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @YES, NSInferMappingModelAutomaticallyOption: @YES };Bimetallic
H
6

Yes, you may use multiple stores for a single model, but you can't create relationships between objects in different stores. Look for the Cross Store Relationships section in Core Data Programming guide, which says essentially that and recommends using fetched properties if you need to relate an object in one store to an object in another.

Hanaper answered 8/3, 2011 at 12:13 Comment(2)
Having a hard time getting a working example from the docs. From the description, this is the way to go. But what does the model look like in that case? Do you use two different managed object models as well? Or one model with two stores?Bimetallic
Rephrase: How do you specify in the model editor which entities belong to what store? Does it work like that at all?Bimetallic
D
2

One thought: You might want to create different stores altogether and also different persistent store coordinators for each of the store. And then create different managed object contexts for each of model part. So let us say, I have a model with 3 entities: Student, college and courses. Suppose that I want to store Student and college entities in store1, and Course in Store2, I would have 2 sets of managedObjectContext, pesistent store and persistent cordinator. Now given that we can not have any cross store relatioinships, modification in one context do not effect another context. You dont have to create different models or associate them to stores -etc.

Depicture answered 13/4, 2011 at 9:1 Comment(1)
Yes, that is what I ended up doing. The thing is though that I wanted to be able to use the Xcode modeling tool to have one complete overview of data the app uses. But I didn't get that to work. :SBimetallic

© 2022 - 2024 — McMap. All rights reserved.