Core Data: Re-assign parent entity - ios

Using Core Data, what's the proper way to re-assign the parent entity?
I have a Car entities that has one-to-many relationship with wheels and is cascaded delete (if the car is deleted, all its wheels are also deleted). Each wheel can only have one Car parent. How do I re-assign the wheel to a different Car?
For example:
Car-A has Wheel1, Wheel2, Wheel3, Wheel4 as its children.
I want to move Wheel1, Wheel2, Wheel3, Wheel4 to a new parent called Car-B.
Here's what I've done:
for (Wheel *wheel in carA.wheels) {
wheel.car = carB;
}
However, the above throws error:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <_NSFaultingMutableSet: 0x7fe0537dd2b0> was mutated while being enumerated.'
Besides the error being thrown, I also feel simply re-assign the parent entity as above is not the proper way.
Any advice would be greatly appreciated. Thanks.

You are mutating the set directly within the for-loop which leads to the error. Assuming, the set is a NSMutableSet, the proper way to do this would be:
NSArray* wheels = [carA.wheels allObjects];
for (NSManagedObject* wheel in wheels)
{
[carA removeObject:wheel]
[carB addObject:wheel]
}

You cannot mutate a collection that you are enumerating. If you think about it, it is quite logical that this has to lead to problems.
The solution is to separate the items first.
NSSet *wheels = carA.wheels
carA.wheels = nil;
for (Wheel *wheel in wheels) {
wheel.car = carB;
}

Related

Illegal attempt to establish a relationship 'object' between objects in different contexts

I know that several issues possibly duplicated being, however, no pointed solution solved my problem, so I decided to post my specific case.
I'm working with CoreData in my application, and some objects are instantiated without being effectively saved on the ground, my startup code in these cases is as follows:
-(id)initEntity:(NSManagedObjectContext*)context{
AppDelegate appDelegate * = [[UIApplication sharedApplication] delegate];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Endereco" inManagedObjectContext: appDelegate.managedObjectContext];
self = (Endereco*)[[Endereco alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
return self;
}
However, an attribute of this object is the municipality that is already saved on the base, and is selected by a ActionSheet:
if (actionSheet == actionSheetMunicipios) {
Municipio *municipio = [municipios objectAtIndex:buttonIndex-1];
endereco.municipio = municipio;
[textMunicipio setText:endereco.municipio.nome];
}
in line
endereco.municipio = municipio;
I get the following error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Illegal attempt to establish a
relationship' municipio 'between objects in different contexts.
The error is clear, I am trying to establish a relationship of objects with different contexts, but in my case, in which the Parent object is not saved on the base, and that the child object is already there, how could I solve?
Your comments seem to indicate you know the answer. Add endereco to the context (use insertIntoManagedObjectContext: context rather than insertIntoManagedObjectContext: nil). It's not a matter of being saved; you need to make sure that the two objects are in the same context. There is no way around that. You cannot create cross-context relationships in properties (you can in fetched properties, but it's complicated and this doesn't seem like a case where you want it).
I managed to solve the problem by adding the Endereco in the managedContext of the Municipio:
if (actionSheet == actionSheetMunicipios) {
Municipio *municipio = [municipios objectAtIndex:buttonIndex-1];
[municipio.managedObjectContext insertObject:endereco];
[endereco setMunicipio:municipio];
[textMunicipio setText:endereco.municipio.nome];
}
I do not know if it's the best solution, but it worked perfectly in this case.

Core data subclass ManagedObject's to-many relationship addObject can't work

I have two subclass of ManagedObject as below
which is one-to-many relationship between them.
In order to add definition to word, I implement the func below:
func addDefinition(definition: MODefinition) {
self.mutableSetValueForKey("definitions").addObject(definition)
}
But when I run the function, it crashed and show message below:
'NSInvalidArgumentException', reason: '*** -[NSSet initWithArray:range:copyItems:]: array argument is not an NSArray'
I also try to assign word to definition like :definition.word = word
But it appear the same crash message.
However, I can assign NSSet to word.definitons like this:
let definitionSet = NSSet(array: definitions)
word.definitions = definitionSet
But this method will not let me append object...
Also, my other managedObject can use this kind of functionself.mutableSetValueForKey to addObject. So I don't know where did it go wrong.
EDIT:
I'm trying to removeObject :
func removeDefinition(definition: MODefinition) {
let muSet = self.mutableSetValueForKey("definitions")
muSet.removeObject(definition)
}
And It shows
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSSingleObjectSetI objectAtIndex:]: unrecognized selector sent to instance
I'd trying to implement on other objects, but only this object can't work.
What should I check for this problem, I'm really stuck now.
EDIT2 :
I add the new relationship to test if the problem is due to the relationship.
So, I added the test relationship.
And.... It work...
Although, I still don't know the reason, I can run normally now.
i'm also new to ios.hope this will help you.
make NSManaged in Moword class.
like:
#NSManaged var word: NSSet?
and then make NSMutableSet object
let wordMutableSet = NSMutableSet()
and every time create new object of MoDefination
let entity = NSEntityDescription.entityForName("MoDeination", inManagedObjectContext: Utilities.getAppManagedContext())
let obj = MoDefination(entity: entity!, insertIntoManagedObjectContext: Utilities.getAppManagedContext())
wordMutableSet.addObject(obj)
and add it now in Moword object
Moword entity1 = NSEntityDescription.entityForName("Moword", inManagedObjectContext: Utilities.getAppManagedContext())
let obj = Moword(entity: entity!, insertIntoManagedObjectContext: Utilities.getAppManagedContext())
obj?.scores = wordMutableSet

Core Data one to one relation ship throws error

I have core data model like in below images, and need one to one relation ship.
and when I am trying to add record in core data entities.
#import "ViewController.h"
#import "MagicalRecord.h"
#import "Child.h"
#import "Parent.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Child *child = [Child MR_createEntity];
child.name = #"Child";
Parent *parent = [Parent MR_createEntity];
parent.name = #"parent";
parent.child = child;
}
Xcode will throws error like below.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-many relationship: property = "child"; desired type = NSSet; given type = Child; value = <Child: 0x7feec35bc840> (entity: Child; id: 0x7feec359af30 <x-coredata:///Child/tC70A9E87-948D-4407-AE19-73F9661A29372> ; data: {
name = Child;
parent = (
);
}).
Go to the Data Model editor (graph editor style), select Parent's relationship to Child and make sure that your relation looks a below:
From the error message it seems that you have defined the relationship as "to-many". This is why it is expecting an NSSet for relationship child (where indeed children would be more appropriate).
Assuming that you want a to-many relationship (i.e. a parent can have more than one child), rename the relationship to the plural as mentioned above, and make sure the model looks like this:
Parent <--------->> Child
Then the simplest way is to just set the to-one relationship:
// add a child to a parent
child.parent = parent
Obviously, if for some reason you do want a one-to-one relationship, check that the model reflects that as well.
Parent <----------> Child

How do I add to and update a many to many relationship in Core Data?

I have a two core data models set-up that have a many-to-many relationship.
class BuddyCD: NSManagedObject {
#NSManaged var memberOfBunches: NSSet
}
class BunchCD: NSManagedObject {
#NSManaged var bunchMembers: NSSet
}
Sidenote: CD means Core Data here
I when creating a BunchCD, I would like to add many buddies to it as members.
I have this method in the Bunch class:
class func createInManagedObjectContext (moc: NSManagedObjectContext, members: [BuddyCD]?) -> BunchCD {
let newBunch = NSEntityDescription.insertNewObjectForEntityForName(CoreDataConst.bunchModel, inManagedObjectContext: moc) as! BunchCD
if let membersNonOptional = members {
// Add members to this bunch
for member in membersNonOptional {
member.addToBunch(newBunch)
}
}
return newBunch
}
I have this method in the Buddy class:
func addToBunch(bunch: BunchCD) {
var bunches = self.mutableSetValueForKey("memberOfBunches")
bunches.addObject(bunch)
}
I am getting this error:
2015-07-19 01:00:38.587 MyApp[34979:1885727] -[__NSSetIobjectAtIndex:]: unrecognized selector sent to instance 0x7fb7d3ecde30 2015-07-19 01:00:38.603 LunchBunch[34979:1885727] ***Terminating app due to uncaught exception'NSInvalidArgumentException', reason: '-[__NSSetI objectAtIndex:]: unrecognized selector sent to instance 0x7fb7d3ecde30'
What is the best practice for adding members to a many to many relationship in core data?
Your code seems OK. The error must originate elsewhere. Perhaps you can find out where by stepping through the code.
So the error originated elsewhere, I'm guessing from multi-threaded components of my application (that were not intentional). The errors most likely stemmed from using Alamofire asynchronous requests and using the Managed Object Context in the request callbacks. So, I added code around the Managed Object Context accesses to ensure it (the MOC) was being used on the main thread:
dispatch_async(dispatch_get_main_queue()) {
// Managed Object Context access here
}

GenericException : Collection was mutated while being enumerated

I have searched for this problem and I know it is caused because an array is manipulated while its enumeration. Earlier in my code, I was using :
-(BOOL)busy
{
for(DataRequest* request in self.active)
if(!request.invisible)
return YES;
return NO;
}
and -(BOOL)busy is being called very frequently as the data loads from the server. Also in my code I have some lines which adds and removes objects in self.active. And because of this I was getting the exception. Then I made a change in the code as:
-(BOOL)busy
{
self.tempActive = self.active;
for(DataRequest* request in _tempActive)
if(!request.invisible)
return YES;
return NO;
}
but I am still getting the same exception. What am I doing wrong, any ideas? This is the only code where self.tempActive is used.
The exception I am getting is:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x9ec8f60> was mutated while being enumerated.'
You need to understand the difference between classes and stack objects (like ints and things). Setting something equal to something else does not make a copy of it if it is a class. You need to use [self.active copy] instead.

Resources