2

pretty new to objective-c and VERY new to CoreData - this question feels ridiculously simple, but I can't figure it out despite 1.5 hours of searching! looking for greater minds.

situation: have an Entity in CoreData ("AssetType"), and that entity has one Attribute ("label"). AssetType has a to-many relationship with another entity ("Items"). Items has a to-one relationship to AssetType there are currently 3 values possible for a "label" - "Electronics", "Furniture", "Jewelry".

goal: very simply, i would like to remove one of the values from all objects. i would prefer this be done all in one go instead of via a for-loop on the "many" objects (eww), but truthfully i'm just lost in CoreData and syntax so whatever you can provide would be awesome.

code structure/background: I can paste more in if needed, but i'm using a generic UITableView + UINavigationItem editButtonItem to execute edits (within a UIPopoverController if that matters) - that's where the delete method is coming from - and i'm capturing it via the tableView:commitEditingStyle:forRowAtIndexPath: delegate/protocol method. this part is not the problem - i know where to put the code, i'm just lost in CoreData. :(

twist: AssetType does not currently have a class - it exists purely as a property in the Item class (and a separate entity in CoreData), which has been fine up until now.. but maybe when i need to manipulate the attributes (e.g. delete them!), this is when i need to introduce its own class? hoping that's not the case.

thanks folks!

toblerpwn
  • 5,415
  • 8
  • 38
  • 46

2 Answers2

2

Alternatively if you actually want to remove the AssetType altogether, you can set a delete rule in your model for the reverse relationship from AssetType back to Item. In this case it sounds like you would want a Nullify rule,

Then you would simply delete the AssetType object in question which would then automatically nullify all links from Items that had that AssetType on the next save.

enter image description here

// Get descriptions of our entities so we can create some.
NSEntityDescription *assetTypeEntityDescription = [NSEntityDescription entityForName:@"AssetType" inManagedObjectContext:self.managedObjectContext];
NSEntityDescription *itemEntityDescription = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:self.managedObjectContext];

// Create some asset types
AssetType *furnitureAssetType = [[AssetType alloc] initWithEntity:assetTypeEntityDescription insertIntoManagedObjectContext:self.managedObjectContext];
AssetType *electronicsAssetType = [[AssetType alloc] initWithEntity:assetTypeEntityDescription insertIntoManagedObjectContext:self.managedObjectContext];
AssetType *jewelryAssetType = [[AssetType alloc] initWithEntity:assetTypeEntityDescription insertIntoManagedObjectContext:self.managedObjectContext];

furnitureAssetType.label = @"Furniture";
electronicsAssetType.label = @"Electronics";
jewelryAssetType.label = @"Jewelry";

// Create some items
Item *item1 = [[Item alloc] initWithEntity:itemEntityDescription insertIntoManagedObjectContext:self.managedObjectContext];
item1.assetType = furnitureAssetType;

Item *item2 = [[Item alloc] initWithEntity:itemEntityDescription insertIntoManagedObjectContext:self.managedObjectContext];
item2.assetType = electronicsAssetType;

Item *item3 = [[Item alloc] initWithEntity:itemEntityDescription insertIntoManagedObjectContext:self.managedObjectContext];
item3.assetType = jewelryAssetType;

[self.managedObjectContext save:nil];

NSLog(@"item1 asset type is:%@", item1.assetType.label);
// Output: item1 asset type is:Furniture

// Delete the furniture asset type
[self.managedObjectContext deleteObject:furnitureAssetType];

NSLog(@"item1 asset type is:%@", item1.assetType.label);
// Output: item1 asset type is:Furniture

// Save the changes..
[self.managedObjectContext save:nil];    

NSLog(@"item1 asset type is:%@", item1.assetType.label);
// Output: item1 asset type is:(null)
// Because of the delete rule when the furniture object is deleted relationships that pointed to it are nulled out.  
// The furniture asset type no longer exists.  
// There are now only 2 asset types in the persistent store.
Rory O'Bryan
  • 1,894
  • 14
  • 22
  • thanks rory - so it sounds like I would make an NSEntityDescription entity (AssetType in my case), and then set up a NSRelationshipDescription for the same Entity (AssetType), and then delete one of the values associated with the "label" Attribute.. but how might I delete the value (e.g. "Furniture") in the first place? i'm stuck on the VERY basics of this process unfortunately. :) – toblerpwn Jul 10 '12 at 16:57
  • I've added example code and a model to illustrate the point. In this example I have built my model using the model editor (recommended!) and generated subclasses from the model `Editor->Create NSManagedObject Subclass..` so I can use properties and type names etc. – Rory O'Bryan Jul 10 '12 at 19:11
  • As for the twist you added. Assuming you mean you have an `AssetType` entity but you are just grabbing the label text from it when the user selects it and setting that as a string property on `Item`?, then yes you'll need a proper relationship as illustrated above to take advantage of the automatic features of core data shown here and also in Tommy's alternative method (which achieves similar results but doesn't delete the `AssetType`) – Rory O'Bryan Jul 10 '12 at 19:21
  • thanks, rory, this is massively helpful. QUESTION: currently, i am only saving on `didEnterBackground` - will I need to include more saving throughout my application? or will CoreData allow me to handle this in memory - e.g. if I delete a label using the method above, should I be able to use `deleteRowsAtIndexPaths:withRowAnimation:` to re-draw a UITableView? i.e. if I had 6 AssetTypes, then delete one using the method above, will the table know that there are only 5 now? i assume yes, since i can even now ADD labels without executing a CoreData save. – toblerpwn Jul 10 '12 at 19:45
  • (for what it's worth, using your method WITHOUT a dedicated AssetType class will actually work all the way up until it is time to redraw the table during `deleteRowsAtIndexPaths:withRowAnimation:` - at that point, if I request all of the labels, it returns them all fully (e.g. I've got 5 items listed like this: `(entity: AssetType id: ... ; data: {\n items = \"\";\n label = Furniture;\n})`), **except** the deleted one which is still present but reads `(entity: AssetType; id: ... ... ; data: )` (notice that data is completely in the item i deleted) – toblerpwn Jul 10 '12 at 19:48
  • OK, when you say "without a dedicated AssetType class" I assume you mean without generating the subclass, just using `*NSManagedObject` types not `*AssetType`? If so it's worth pointing out that the subclasses are just a convenience everything can be done without them. I prefer them to make the code more readable etc and I understand it aids performance as well. – Rory O'Bryan Jul 10 '12 at 20:21
  • With regards to the other question, as shown in the code sample the object is still present until the save is executed. However it will be flagged as deleted in it's `isDeleted` property. So you could carry on without the save but would need to take the `isDeleted` property into account when responding to the `UITableView` call backs. I would suggest though that you want to be saving a lot more often with core data, have a look through some of the sample apps e.g. recipes to see when they save. – Rory O'Bryan Jul 10 '12 at 20:32
  • re: "without a dedicated AssetType class" = yes --- re: my other problems I'll keep chugging along :) thanks again man – toblerpwn Jul 10 '12 at 22:16
0

Just to be clear: you've two entities, "Asset Type" and "Items", which have a many-to-many relationship. You want to have all items sever their connection with a certain asset type simultaneously and, ideally, you don't want to iterate through items. Have I got it?

If your asset types are genuinely unique (so, there's exactly one that has the label 'Furniture', for example) then you can just unlink that way around — relationships in Core Data are always bidirectional and unlinking from one side automatically unlinks from the other. NSManagedObject subclasses also have accessors that can add or remove things at the end of 'to many' relationships in sets. So e.g.

[furnitureObject removeItems:furnitureObject.items];

That is assuming that your asset type entity has a 'to many' connection named 'items' that links to items.

Otherwise you can run a predicate to get the list of all assets with the string name 'Furniture' and iterate through those, I guess.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • Thanks Tommy - you got it, except that Items is a **to-one** relationship with Asset Type (Items can only have one Asset Type), but Asset Types is to-many (I might have 5 different Items that are all 'Computer' Asset Types). Does that change the solution? – toblerpwn Jul 10 '12 at 03:44
  • Shouldn't, unless the objective is to delete all Items with a certain Asset Type, not merely to sever the link with that asset type? – Tommy Jul 10 '12 at 17:17
  • nope - the objective is simply to remove the AssetType completely (so that while setting the AssetType for Item #1, if you delete Asset Type #6, it is deleted and removed for all Items). i'm having trouble getting the method above to work (compiler errors).. i'll keep hunting. what code snippets would help more clearly outline what i've got going on? this feels like it touches a lot of code so i don't want to paste in my whole project. ;) – toblerpwn Jul 10 '12 at 17:25
  • also i added a 'twist' above that outlines one detail i forgot last night - in case it matters – toblerpwn Jul 10 '12 at 17:26