Save objects from multiple stores to single persistent store - ios

I have two persistent stores with objects that use the same model. I would like to open both stores in one context, but save the context to only a single store, and then safely delete one of the stores. I am in essence trying to merge the contents of two persistent stores into a single persistent store. Because the entities have relationships, I am finding this difficult.
From this answer:
If you do need to have relationships between the objects in both stores, or you really just want to have a single store, your best bet would be to create a second NSPersistentStoreCoordinator and a third, distinct persistent store to hold the merged object graph. You will need to write code to create copies of the objects in a managed object context attached to this second NSPersistentStoreCoordinator. You'll need to set up the same relationships among the copies that the original objects had, too, but how you go about doing this depends on your data model.
That makes it seem clear, except for the details of how, literally, to make copies of the objects for the new store. Is this a migration issue?

Ultimately, I used the following approach:
[migrator migrateStoreFromURL:[NSURL fileURLWithPath:incomingPath]
type:nil
options:nil
withMappingModel:managedObjectModel
toDestinationURL:[NSURL fileURLWithPath:finalPath]
destinationType:nil
destinationOptions:nil
error:&err];
[persistentStoreCoordinator removePersistentStore:[[persistentStoreCoordinator persistentStores]lastObject] error:&err];
The file at "incomingPath" was the store I imported, the file at "finalPath" the already-existent store that I wanted to merge into. Both stores were open in the same persistent coordinator, and both use the same object model. I then removed the incoming store and never looked at it again; I suppose I could have deleted it at the file system level.
For my particular data needs, I then fetched all the records, culled out the duplicates, and saved the context.
I publish this answer because as a core data newbie this simple migration solved the "can't save relationships to objects in different stores" problem non-intuitively.

Related

CoreData: saving data to context

I am working on to use CoreData to save data of an object to an entity created in the datamodeld file.
In my scenario, I made a datamodeld called Product which represents an item available in the grocery store.
I have several categories for each type of products. For example, produce, dairy, meat, etc... If I save those products in the Product context, they will all be aggregated into one database and retrieving the data will be a hassle.
I'm wondering if it is possible to store my items into different databases of the Product context.
The only workaround I can think of is to create an entity for each categories which is a child of Product. I don't like this workaround because I am creating unnecessarily entities (every products in each categories have the same attributes).
By "product context", I guess you mean managedObjextContext? Your data is not saved in moc, you write data on moc and it's reflected persistent store.
You can use multiple moc to save the data of your app but be extra careful to syncronize the moc to moc on main thread.
I did something on the same lines that you are proposing as a work around. It worked out fine in my case.

Create Core Data entities dynamically during runtime

I need to be able to create new core data entities during runtime. I've written the code to create the objects programmatically, however, I can't add the entities during runtime as the model is immutable.
My problem is similar to this post, however there is no satisfactory answer: How to dyanmic create a new entity (table) via CoreData model?
The documentation regarding changing the core data model explains:
Managed object models are editable until they are used by an object
graph manager (a managed object context or a persistent store
coordinator). This allows you to create or modify them dynamically.
However, once a model is being used, it must not be changed. This is
enforced at runtime—when the object manager first fetches data using a
model, the whole of that model becomes uneditable. Any attempt to
mutate a model or any of its sub-objects after that point causes an
exception to be thrown. If you need to modify a model that is in use,
create a copy, modify the copy, and then discard the objects with the
old model.
However, I'm unclear on what exactly this is saying--that the whole core data model can't be changed once the persistent store coordinator has been used or the attributes/etc of the individual entities can't be changed.
To be clear, I do not want to change the attributes of my current entities, I simply want to add new entities. It just seems weird to me to have to use migration to add new entities.
Any thoughts?
Thanks!
The documentation is pretty clear.
Copy the model.
Apply your changes to the new copy.
Destroy your old MOC, Persistent Store Coordinator, and all objects created from those.
Apply a migration, if necessary.
Create a new Core Data Stack (MOC, PSC, etc) using your updated model.
The migration could be a sticking point, but it should be do-able.

Understanding of NSCoreData and MSManagedObject subclasses

I am learning a bit on NSCoreData and before introducing it some existing projects I have, I would like to validate my good understanding of the core principles.
From what I have understood, NSCoreData make it easier to manage local storage of object (+retrieval after that) by subclassing our Model class from NSManagedObject rather than from NSObject.
Right ?
I have a few questions then. Let's consider I am building a real estate application with as core model object the class Property that can represent an appartment, a house, and all related information. Currently it is managed in my app as a subclass of NSObject.
1) I retrieve the properties from the server through a search query, and have written a initWithJson : method to populate each instance.
Now if I subclass Property from NSManagedObject, I will create my instances by using
+(id)insertNewObjectForEntityForName:(NSString *)entityName
inManagedObjectContext:(NSManagedObjectContext *)context
and I will be still be able to add a populateWithJson: to my class to fill in the properties.
Then I will create a lot of Property instances in the current managedObjectContext, and if I do a save, they will be stored at the physical layer.
If I call again the same webservice, and retrieve the same JSON content, I will recreate the identical managed objects.
How to avoid redundancy with the [managedObjectContext save:&error] call and not to store physically several time the representation of a single real life property ?
2) Let's say I want to store physically only some properties, for instance only the one the user want to have as favorites.
[managedObjectContext save:&error] will save all created / modified / deleted managed objects from the context to the physical layer, and not only the one I want.
How to achieve that ?
Am I supposed to declare another context (managedObjectContext2), move the instance I want to store in that context, and do the save in that one ?
(I mean, I will have a context just to manipulate the object, create instances from the JSON and represents them in UI ... and a second one to actually do the storage)
Or am I supposed to stores all the objects, and add a isFavorite BOOL property , and then fetching using a predicate on that property ?
3) The app has a common navigation pattern : the UITableView lists Properties instance with the minimum information required, and going on a detail view call a webservice to request more information on a specific Property instance (images, full text description).
Is it a good practice for instance to call the webservice only if the property.fullDescription is nil, and then update the object and store it locally with all detailed information, and the next time only to fetch it locally with a predicate on the property.id ?
What about object that might be updated server-side after they have been created?
Thanks for your lights
1) Retrieve the server data into a temporary form (array of dictionaries?), then for each possible property in the array, check to see if you already have an object in Core Data that matches. If you do, either ignore it or update any changed attributes; if not, create a Property object.
2) Decide which things you want to persist in order to support your app's functions. There's no point in creating a managed object for something you don't want to save. Note, though, that Core Data supports sub-classes if you want both Property and FavoriteProperty.
3) Entirely up to your "business rules"…. How often do you need local data to be updated? The only technical consideration might be the guideline to not keep large files locally that can be re-created on demand.

Core Data: Turning Objects into Faults after saving them?

In my code I do a large import of thousands of objects and execute save after each object .
Do I need to turn each object into faults after saving it to save memory or will Core Data turn it automatically into faults? Unfortunately, I have not found any clue in Apple`s doc.
Thank you!
A. You can turn an object into a fault with refreshObject:mergeChanges: (NSManagedObject). This will give up strong references to related objects, so that they can be released. (If they are not holded by another reference.)
You can turn a realized object into a fault with the
refreshObject:mergeChanges: method. If you pass NO as the mergeChanges
argument, you must be sure that there are no changes to that object’s
relationships. If there are, and you then save the context, you will
introduce referential integrity problems to the persistent store.
(Link)
B. You can wipe all objects out with -reset (NSManagedObjectContext) as Daniel G said. But this really wipes out the objects, references can break.
C. I think, that there is no promise of CD to turn all saved objects into faults. (Unsaved objects cannot turn into faults.) But simply overwrite -didTurnIntoFault (NSManagedObject) to see the behavior of CD.
I'm not exactly sure what you are asking but if you have a lot of objects hanging around in your context you can always use the method -[NSManagedObjectContext reset] to reset the context and purge memory. You also don't want to do this if you have any objects that reference NSManagedObjects within the recently reset context.
I'm not sure as to the nature of your application or why you would need to execute a save on thousands of objects, it seems that you should look into using batches for your fetch requests. This way core data will guarantee that only a specified number of objects will be living in the context at any given time.
I hope this helps?

NSUserDefaults vs. Core Data for application state

I have an existing large app using plists to store its data. I store application state indicating current item selected, current user, and various other current selections in user defaults. This worked fine when the data was in plists. Now I'm refactoring to use Core Data instead of plists. I want to store a reference to an object instance as the currently viewed object. I know sqlite and Core Data have an ID for this object in the database, but I'm not sure what to store in user defaults. Three options come to mind:
Generate my own sequential ID, store that with the objects I want to remember as "current", store that ID in user defaults.
Try to use NSManagedObjectID and store in user defaults. I "think" I can convert it to a string as follows. Does this work?
NSString *stringID = [[[managedObject objectID] URIRepresentation] absoluteString];
I could create a current state entity in Core Data which is a singleton, only one row. This entity could have a to-one relationship to the current object I want to keep track of.
Suggestions appreciated for the approach with best design consideration.
Repeating from a comment, what are the drawbacks to approach #2? I've read conflicting accounts on the web. Some say the object ID is not consistent across migrations, yet Apple itself seems to suggest this as the solution.
In the Apple Class Reference for NSManagedObjectID, they discuss this very case and talk about saving the ID in user defaults. It says, "Object IDs can be transformed into a URI representation which can be archived and recreated later to refer back to a given object (using managedObjectIDForURIRepresentation: (NSPersistentStoreCoordinator) and objectWithID: (NSManagedObjectContext). For example, the last selected group in an application could be stored in the user defaults through the group object’s ID."
Does that mean the object ID URI representation always stays valid, even across migrations?
You should use your own unique identifier (ideally which will not server only for the purpose of this question) for your objects and use this identifier to retrieve the object from Core Data and store this identifier in the NSUserDefaults. So, yours 1. solution is correct, though it need not to be sequential.
The 3. solution would work also, but it is not very clean.

Resources