Replace entities in to-many relationship in Core Data - ios

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]

Related

Core data with unique constraints and relationships-IOS

I have a core data design with multiple tables using relationships. My database is SQLite. For updates I import data from JSON and use this method:
[NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context].
I have added unique constraints in core data.
If I update an entity that is a relationship of another object it loses the connection.
Ex: Entity "person" that contains the one to one relationship to "pet_id". If I update "pet" it changes his id and "person" still points to the old id, so they are not related any more.
Is there a way to avoid this problem?
I don't think this is documented anywhere yet. Here's what it sounds like is happening:
You create a new instance. Your constraints mean that this instance matches an existing instance. But...
Your new instance has a nil value for this relationship. So...
The existing instance's value for the relationship is replaced by this new nil value.
To maintain the relationship, your new instance needs to already have the correct value for that relationship. You're essentially asking that the constraint matching system ignore the fact that the relationship value is different in your new instance, but to accept new values for other attributes.
I think what you're expecting is completely reasonable but I'm also not surprised that the current implementation doesn't support it. I recommend filing a bug with Apple about this, and investigating non-constraint based approaches to keeping your data unique.

How to automatically delete any entity when all its relationships are nil?

I have a simple Core Data app that has a many to many relationship from entity A to B. I have kept the delete rule as nullify.
I simply want that when all entities of type A that relate to an entity of type B are deleted. In other words, when all relationships from a given B to A are nil, that particularly object of entity B should be deleted.
Now I noticed that, at least for me, this isn't happening automatically. Do I have to manually check to see if a given B object has all relationship to A as nil and then delete them manually or is there an automatic way to achieve it?
In your NSManagedObject subclass
- (void)willSave
{
// Check for relationship and delete self if empty
}
...or is there an automatic way to have this happen?
Yes if you let Core Data to manage this.
If you have two entites, say Parent and Child where the former has a one-to-many relationships with the latter
you can set the children relationship with a Delete Rule Cascade.
On the contrary, the parent relationship would be
The check on the optional flag depends on if a Child could exist with or without a Parent associated with it.
Here the delete rules mean the following.
If I delete a parent, all the children will be deleted. If I delete a child, nothing would happen on the parent (in other words the parent won't be deleted).
The inverse relatioship between the Child and the Parent is very important since lets Core Data to maintain the graph consistency. So, you should (a must for me) use it in every model you have.
Is this what you want to achieve? Let me know if you need something else.
Update 1
Only children can be deleted and not the parent. And I want that when
all children of a given parent are deleted, the parent object should
be removed from the store as well. the parent cannot be directly
deleted.
Deleting a parent it's up to you. In your code you will just not delete a parent if you want. I would use the configuration I provided since, if you delete a child, the parent will remove the reference to it.
To delete all the children that belong to a specific parent I would use a simple fetch request against Child where the predicate would be
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"parent == %#", parentOfTheChildrenYouWantToDelete];
Once run, the request will return a NSArray of managed objects. for in to delete them.
In my understanding of your question, I recommend you look into running a specific fetch at a certain point in your code to check for nil against the relationship, and then delete manually.
So to answer your question, my understanding is that there is no "automatic" mechanism.

Coredata relationship breaks once the entity is updated

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.

Reassigning one-to-one relationship nulls previous object / CoreData iOS

I have 2 entities. ObjectA stores all ObjectB's objects through a many-to-many relationship. ObjectA also stores one specific object as a default object using a one-to-one relationship. The idea is to be able to assign many different child objects for EntityA while also keeping a specific reference to one specific child object. This idea works perfectly fine all throughout my project exempt in one circumstance(identical code and identical entity relationship setups.
The problem I am having is when I reassign the existing defaultObject to a new different object by simply ObjectA.defaultObject = someObject23; this assigns the new object correctly but in the process my original To-Many relationship reference to that existing defaultObject goes null.
The to-many relationship 'AllObjects' from EntityA has a Cascade delete rule for EntityB.
The One-To-One relationship 'DefaultObject' has a NULL delete rule for EntityB.
Both have inverses set.
Here is a real quick overview.
ObjectA.allObjects = 10 objects; // 1 of these is someObject1
ObjectA.defaultSomeObject = someObject1; // This works fine.
ObjectA.defaultSomeObject = someObject2; // This assigns the new defaultSomeObject=someObject2,
// but in the process it removes the someObject1 from my ObjectA.allObjects array (Goes NULL)
I'm stumped because like I say I have used this technique multiple times and the only workaround to this I have succeeded with is to "rig" it and actually save a reference to the previous object, delete it from the ObjectA array, set the new defaultObject, then write that object back to the array. There must be a simple explanation I am overlooking. More coffee? lol. Any help is greatly appreciated. I have tried all the different delete rules for each relationship as well just for kicks.
Problem solved. Definitely needed more coffee. What was happening was the one-to-one relationship was using the to-many relationship inverse causing it to do exactly what it was supposed to do, go null...

iphone coredata removing records between two entities connected by relation ship

I'm implementing core data in my iphone app. It has two entities.
Entity1: LatestData
Entity2: LatestDetailedData
LatestData has URL, publishedDate, heading
LatestDetailedData has URL, NewsDescription, PublishedDate, Author
Both entities have same URL for a record.
Both the entities are connected with inverse relation ship. And the relation ship is "delete->Cascaded"
What I want: If I remove a record in LatestData, I want the record with same URL in LatestDetailedData must also be deleted.
How?
If I am understanding you correctly, you are using a relationship and it has an inverse. If that is the case then when you delete one then Core Data will automatically delete the other and you don't need to do anything extra.
What are you seeing that suggests that is not happening?
Update
Since you are using multiple threads, are you using one NSManagedObjectContext per thread? If so, are you updating all of the threads when a save occurs? I suspect one of those two is not occurring and therefore causing your issue.
when you create the entities you need to create the relationship too
LatestDetailedData * entity2 = [[NSEntityDescription insertNewObjectForEntityForName:#"LatestDetailedData" inManagedObjectContext:context];
entity1.lastestdetail=entity2;
if you are just relying on a URL field, then thats bad practice. Set up the relationship in coredata and the cascaded deletes will take care of themselves.

Resources