Core Data Deletion Unsuccessful *easy fix?* - objective-c

I have a simple XCDataModel that contains one Entity with One Attribute. Essentially, I am saving a series of Dates.
Now, I Know I am adding the NSDates properly because I run a fetch request after adding them and run through the results like so:
for (NSManagedObject *info in fetchedObjects) {
NSLog(#"Name: %#", [info valueForKey:#"attribute"]);
}
And every additional NSDate is accounted for. Example from Log:
2012-06-19 12:40:38.531 Arts Days[47194:16103] Name: 2013-03-27 04:00:00 +0000
2012-06-19 12:40:38.531 Arts Days[47194:16103] Name: 2013-03-01 05:00:00 +0000
2012-06-19 12:40:38.532 Arts Days[47194:16103] Name: 2013-01-01 05:00:00 +0000
Now, when I try to delete an object from Core Data, it proves unsuccessful (by running the same fetch and running through the results again).
Here is the fetch:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:366];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
NSError *error = nil;
[fetchRequest setSortDescriptors:sortDescriptors];
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in fetchedObjects) {
if ([[object valueForKey:#"date"] isEqualToDate:date]) {
[managedObjectContext deleteObject:object];
}
}
if (![managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
Also, the NSLog with Unresolved error contains nil nil...
Do you know what I am doing wrong?

It appears you use two different NSManageObjectContext instances. self.managedObjectContext (an ivar) to set up your fetch request and perform the fetch, but a local instance to do the deleteObject: and save: operations. The local instance managedObjectContext is probably nil and not referring to the same object as the ivar.

Related

iOS: How to Sort with NSSortDescriptor a value from relationship?

I have two objects with a relationship in my Data Model...
Person {Name, position, Title}
Position {position, PositionSort}
Each of the above have their own class files (subclasses of NSManagedObject). I'm trying to sort by PositionSort above. I can currently sort by Position by using the following code.
NSSortDescriptor *sortDescriptorPosition = [[NSSortDescriptor alloc] initWithKey:#"position" ascending:YES];
I am unsure though how I'm able to access and use the PositionSort. Any ideas? Thanks!
UPDATE: Added full function...
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptorState = [[NSSortDescriptor alloc] initWithKey:#"position.positionSort" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptorState, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:#"(companyID == %#)", company.companyID];
[fetchRequest setPredicate:searchPredicate];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"position.positionDescription" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
With the above code I'm getting the following error at Runtime.
2012-08-12 10:29:45.499 College[269:707] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0xd6bd310> valueForUndefinedKey:]: this class is not key value coding-compliant for the key positionDescription.'
I'm not sure you want to sort exactly, but the following NSSortDescriptor can be used to sort the Person objects using the PositionSort value.
NSSortDescriptor *sortDescriptorPosition = [[NSSortDescriptor alloc] initWithKey:#"position.PositionSort" ascending:YES];
perhaps, you looked for this...

Making changes to Core Data fetched array

Here's what I have:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"NoteObject" inManagedObjectContext:appDelegate.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(parentNoteId == %#) AND (noteId!=rootNoteId)", appDelegate.currentNoteId];
[fetchRequest setPredicate:predicate];
NSError *error;
[appDelegate.arrayOfNotes setArray:[appDelegate.managedObjectContext executeFetchRequest:fetchRequest error:&error]];
NoteObject *note=[appDelegate.arrayOfNotes objectAtIndex:0];
[note.arrayOfTags addObject:someObject];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
The fetch works fine, and adding an object to the arrayOfTags works and reflects in the UI. However, when I exit the app and come back, the arrayOfTags is missing the one that I added (but it has the other two, so I know the array is working properly). It's not saving for some reason.
(The NoteObject is a subclass of NSManagedObject, and arrayOfTags is a transformable property of the entity.)
Am I doing something wrong here?
Edit: Here's how I add a new note, which saves just fine, even with the arrayOfTags, and everything is saved when I exit the app and come back in.
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *noteEntity = [[appDelegate.managedObjectModel entitiesByName] objectForKey:#"NoteObject"];
NoteObject *tempNote = [[NoteObject alloc] initWithEntity:noteEntity insertIntoManagedObjectContext:context];
[tempNote.arrayOfTags addObject:#"tag1"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
It's only when I make changes that it's not saved properly..
Did you mean you are using a Transformable attribute?
Does this help:
Core Data saving problem: can't update transformable attribute (NSArray)
Turns out all I had to do was re-set the array after making changes to it:
NoteObject *tempNote = [[NoteObject alloc] initWithEntity:noteEntity insertIntoManagedObjectContext:context];
[tempNote.arrayOfTags addObject:#"tag1"];
[tempNote setArrayOfTags:tempNote.arrayOfTags]; //this is the magic line
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}

Why do I get a NSInvalidArgumentException?

Why do I get a NSInvalidArgumentException with my code below?
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Order dish]: unrecognized selector sent to instance
(void)fetchDishRecords: (NSMutableArray*)array predicate: (NSString*)categoryname
{
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Order" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
//set predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Category=%#", categoryname];
[request setPredicate:predicate];
//set sorting
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Dish" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults)
{
// Handle the error.
}
else
{
for(int i=0;i<[mutableFetchResults count];i++)
{
Order *orderObj = [mutableFetchResults objectAtIndex:i];
//NSLog(orderObj.dish);
//NSString* tempStr = orderObj.dish;
[array addObject:orderObj.dish];
}
}
// Save our fetched data to an array
[mutableFetchResults release];
[request release];
[array addObject:orderObj.dish];
Seems to be the culprit...
orderObj is of class Order and the error says it cannot perform selector dish.
Make sure you have a method -(void)dish implemented for your Order class.

Detect updates on sectionNameKeyPath from NSFetchedResultsController

Here is the case. I have 2 models:
Sport (with attribute name and relationShip players)
Player (with attribute name and relationShip sport)
In a ViewController, I wan't to display all players in a UITableView ordered by sport.
When a player name changes, NSFetchedResultsControllerDelegate callbacks are called.
But when the name of a Sport is updated, no NSFetchedResultsControllerDelegate callbacks are called.
Is this a normal behaviour ? How can I know the updates of Sport.name (without creating another NSFetchedResultsController) ?
Thank you and Best Regards.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"sport.name != NULL"];
[fetchRequest setPredicate:predicate];
NSArray *sortDescriptors = [NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:#"sport.name" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES],
nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Player"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entityDescription];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"sport.name"
cacheName:nil];
[fetchRequest release];
fetchedResultsController.delegate = self;
NSError *error = nil;
if ([fetchedResultsController performFetch:&error] == NO) {
ALog(#"Fetch error: %#", [error localizedDescription]);
}
You can using a tip:
If (fetchresultcontroller != nil || isOtherEnthityUpdate)
{
return fetchresultcontroller;
}
And do self.tableview reload from any place where u check other entity. It can be notification center or other tableview.

How do you create a Set of Entities which all have Relations to other Entities with Specific Attributes? In Core Data

I've got a Core Data entity called "Card" which has a relationship "info" to another entity, "CardInfo". It's a one-to-many relationship: each card can have multiple CardInfos, but each CardInfo only has one Card.
The CardInfo entity just has two strings, "cardKey" and "cardValue". The object is to allow for arbitrary input of data for cards. Say, you wanted to know what color a card was. Then you added a CardInfo to each Card that had a cardKey of "color" and a cardValue of "black" or "red".
My general question is: what's the best way to get the set of Cards where each Card has a CardInfo where the CardKey and CardValue has specific values. For example: all Cards with relationship to CardInfo cardKey='color' and cardValue='red'? Ideally, I return an NSSet of all the appropriate Card * objects.
The loop at the end is not needed. A simple KVC call will clean it up nicely.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"CardInfo" inManagedObjectContext:self.managedObjectContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"cardKey = %# AND cardValue = %#", thisKey, thisValue]];
NSError *error = nil;
NSArray *items = [[self managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release], fetchRequest = nil;
NSAssert1(error == nil, #"Error fetching objects: %#\n%#", [error localizedDescription], [error userInfo]);
return [items valueForKeyPath:#"#distinctUnionOfObjects.card"];
This is the answer that I came up with, but the two-part process seems inefficient to me. I figure there must be a more elegant way to do this with key-values or something
-(NSSet *)cardsWithCardKey:(NSString *)thisKey cardValue:(NSString *)thisValue {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CardInfo"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"(cardKey=%#) AND (cardValue=%#)",
thisKey,thisValue];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *items = [self.managedObjectContext
executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
NSMutableSet *cardSet = [NSMutableSet setWithCapacity:[items count]];
for (int i = 0 ; i < [items count] ; i++) {
if ([[items objectAtIndex:i] card] != nil) {
[cardSet addObject:[[items objectAtIndex:i] card]];
}
}
return [NSSet setWithSet:cardSet];
}