I'm in the process of removing RestKit from our iOS app. I'm able to get things that I want into Core Data, but they're not really connected.
For example, we have one network call that returns a list of "Category"s (which have a "categoryID" and a "categoryName"; "Category"s also map to-many "StoreLocation"s). We then have another network call that returns a list of "StoreLocation"s (which, among other things, have a "storeName", "storeID", "storeCategoryIDs"; "StoreLocation"s also map to-many "Category"s).
With RestKit, I could use a RKConnectionDescription to describe that "storeCategoryIDs" drove the relationship to-many "Category"s. With that, if I had a given Category object, I could easily determine which StoreLocations belonged to that category.
I'm struggling to see how to accomplishing this without any RestKit dependencies. I suppose I could, whenever I'm about to insert a new Category or new StoreLocation, fetch all of the opposite managed objects and do this manually, but I seem to be missing some component of Core Data that can do.
The main part you're missing is the predicate applied to the fetch and which uses the identification attributes to find the appropriate existing objects. You do need to run your own fetch as core data will not magically update one object if you create a different new object and insert it.
Related
I am working on an events app that syncs "Events" with a JSON API using Restkit. The mapping looks roughly like this.
var eventsMapping = RKEntityMapping(forEntityForName: "Event", inManagedObjectStore: managedObjectStore)
eventsMapping.identificationAttributes = ["eventID", "name", "eventDescription"]
eventsMapping.addAttributeMappingsFromDictionary([
"id":"eventID",
"title":"name",
"description":"eventDescription",
(more mapping attributes here, etc...)
])
The events are displayed using a NSFetchedResultsController. Locally, there is a 'User' NSManagedObject that is created...however this object is just local to the app and is not synced to the server via Restkit. The "User" has a one-to-many relationship with Events, with the purpose that a user can "save" events that they are attending. (Note: Right now, both sides of the Core Data relationship delete rules is set to "no action.") The save is roughly done this way.
var managedObjectContext = RKManagedObjectStore.defaultStore().mainQueueManagedObjectContext
currentUser.mutableSetValueForKey("events").addObject(event)
managedObjectContext?.saveToPersistentStore(&error)
So far everything works great, selected events are stored and saved successfully with the user and persists through app relaunches as expected. However, there is one scenario that causes an event to be removed from the user and that is when an update is made to that particular event on the server. When Restkit detects this and updates the event, according to breakpoints I placed in the NSFetchedResultsController didChangeObject, apparently Restkit and/or Core Data is actually deleting the event and then inserting it back with the updates. This is transparent and just fine in most cases, but in this case I'm thinking the initial delete is what is breaking off the event from the user.
Of course, the eventsMapping above doesn't reference any relationship to a user in any way, so that could be another reason why the relationship is broken off. I have been reading more about Restkit relationships and I have used relationship / property mappings in Restkit before successfully to relate objects, but in that scenario both objects existed on the API. In this case, the User here isn't a part of the API at all, only local as I explained. So should I still be using a Restkit relationship mapping? Or perhaps I should be trying to accomplish all of the above via another way?
I figured out the answer, I made a goof in the code above. On the identification attributes for the mapping, I had three different properties in there, when I only should have used the one with the primary key that would never change (eventID).
eventsMapping.identificationAttributes = ["eventID"]
What apparently was happening was because I specified the title/name as an identification attribute, whenever that title changed on the server Restkit would identify it as a new/different object and delete the "old" object and insert the "new" one. When I changed it only to specify the primary key, it triggered an update instead and my relationship with the user persisted.
One more note for others that threw me at first: some older info I came across that helped me solve this said to use a primaryKeyAttribute on the mapping. This is apparently dated information: use the identificationAttributes instead.
I'm making a simple bank account tracker, for self-instructional purposes. I'm using Core Data to store three entities, related as in the screenshot:
WMMGTransaction objects are simply stored as they are recorded, and extracted as needed to feed tableviews and detail views. This will be done via NSFetchedResultsController and a predicate. I'm using MagicalRecord to access Core Data, if that matters.
My question is this:
When I pass WMMGAccount data from one VC to another, such as when creating a new account, or when selecting one from a list (via delegation as a rule), does it matter if I pass a reference to the entire entity, or can I just use an NSString bearing the .name of the account and identify the account when required with a predicate and an NSFetchedResultsController? I guess this is a strategy question, and may generate discussion, rather than having a cut and dried answer, but I'm wrestling with it, so I thought I'd ask.
It sounds like you're asking if you should pass an object to the code that needs it, or if you should pass information that could be used to look up the same object again.
Unless you need to use the managed object on a different thread or queue, you should always pass the actual object. No sense re-fetching an object you already have. It's extra work and code complexity that (unless there are some unusual extenuating details you didn't mention) won't help in any way.
If you are needing to use the object on a different queue or thread, passing information that can be used to look it up is the correct approach. But in that case-- don't pass the value of one of the properties. Use the managed object ID.
Core Data won't force name values to be unique, while the object's managedObjectID is unique. It's also faster when retrieving the object, because you can use objectForID: or existingObjectForID: instead of performing a fetch.
I am using coredata to save the server data through web services in my application and I am storing relationships as an object to the entity.
I have many entities e.g "Inspirations" and "Products" and both are related to each other. I have a problem whenever the records are updated in the third entity which is "Filters" then the relations of the entities broke and I cannot apply filters on the entities.
[object addRelatedInspirationsObject:related];
This is how I save relationships. I am not able to figure out why the relations are being broken once the entity is updated which has no direct link with the entity.
One thing more if I fetch and save the data of any one of the entities like "Inspirations" then all the relations start to work again.
Your code should work. Here are 2 things you need to check:
Make sure related is not nil when you call your method.
Make sure you call save on a valid managed object context.
From your question it seems that entities have 1 to many relationship between them. And by the code you supplied, every things should work fine. Just make sure, you are using the Filter object from the relationship like object.filter (or obj1.obj2.filter), not accessing it via a direct NSPredicate on Filter entity and updating it. And if you are using FRC, you might also need to generate a fault against the parent entities, to get your UI updates.
I am using CoreData in my app and i have a set of "Card" entities. A player can have more than one of the same card in his deck (it is still the same card pulled from the database but added two times to an array).
My problem occurs when I want to modify an aspect of one of the duplicate cards. They are all subclassed NSManagedObjects which have some custom properties on them (which are not saved onto the database). For example when I set one of the custom properties on one card in the array it is also changed in the other same card in the array because the entityForName:inManagedObjectContext: returns the same object and does not load a new one.
Basically what I need is that each time entityForName:inManagedObjectContext: is called I get a new instance of the same entity so that when I modify a custom property in one it is not also modified in the other. I have already tried using [entity copy] if the entity has already been created but it does not work.
Thank you in advance for your help!
Core Data is a persistent store, not a database. So it's reason detre is ensuring that you get exactly the same object out, no matter how many times you ask for it. Those aren't snapshots from the database as they might be if you wrote some custom SQL code, those are the actual live objects.
With that in mind, what you need to do is either configure your Core Data schema to match your logical schema — I guess you'd have, say, CardInstance, with a one-to-many relationship with Card, and you'd create CardInstances for when you pulled a card from the deck — or write some code to read from a fetched Card into a snapshot object, exactly as if you were working manually with SQL or whatever.
When I retrieve an entity from the one side of a one-to many relationship, I create a mutable array from the set that is the collection of entities from the relationship. I manipulate, edit or otherwise change those entities, possibly delete existing or add new.
When through with the changes I simply use the array to create a new set then replace the original set with that which I created like so:
self.myOneSideEntity.theManySideEntitiesRelationship = [NSSet setWithArray:myNewArrayOfEntities];
It occurred to me that replacing the set may not be deleting the old members. What happened to them? Is this the proper way to edit the collection of related objects? Am I leaving any kind of orphans or going against best practices with this technique?
My relationship is set up with an inverse, cascade delete on the one side, nullify on the many side and the inverse relationship is not optional.
I've spent some days to understand similar behavior in my application.
Relation's "Delete Rule" works only when the object that contains relation is deleted itself. If you simply replace one set of objects with another (as you do) - nothing happens. Child objects that were in old set will simply have inverse relations set to nil. So if that relation (from child side) is not optional, you will get CoreData error when saving context.
For now I didn't find any way to manage this, except manual deletion of old objects.
For me the issue was with getting objects which were wired with current object. (groupObject.docs)
It was solved when I add context by which I get this data.
I'm using MagicalRecord:
[GroupObject MR_findAllInContext:[NSManagedObjectContext MR_defaultContext]]
instead of
[GroupObject MR_findAll]