Core Data: keypath name not found in entity - objective-c

I'm crashing with this message :
'NSInvalidArgumentException', reason: 'keypath name not found in entity
Obvisouly I'm not querying my entity correctly .
//fetching Data
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Viewer" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSString *attributeName = #"dF";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name like %#",attributeName];
[fetchRequest setPredicate:predicate];
NSLog(#"predicate : %#",predicate);
NSError *error;
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
NSLog(#"items : %#",items);
[fetchRequest release];
//end of fetch
And here is my data Model:
I want to return the value of "dF", shouldn't call it like this ? :
NSString *attributeName = #"dF";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name like %#",attributeName];

If you want to get value from your dF property, you have to fetch an array of NSManagedObjects and then use [fetchedManagedObject valueForKey:#"dF"]; to get your value.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Viewer" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
NSManagedObject *mo = [items objectAtIndex:0]; // assuming that array is not empty
id value = [mo valueForKey:#"dF"];
Predicates are used to get array of NSManagedObjects that satisfy your criteria. E.g. if your dF is a number, you can create predicate like "dF > 100", then your fetch request will return an array with NSManagedObjects that will have dF values that > 100. But if you want to get just values, you don't need any predicate.

I was using a NSSortDescriptor initialized with a String key:
let fetchRequest = NSFetchRequest<SomeManagedObject>(entityName: "SomeManagedObject")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
I then changed the name of the name attribute in the model. Refactored all of the property names, but didn't catch the stringly typed key: "name".
The solution for Swift is to use NSSortDescriptor(keyPath: instead, which will fail to compile if the property key path changes.
NSSortDescriptor(keyPath: \SomeManagedObject.firstName, ascending: true)

Related

NSPredicate to fetch last message for each conversation in core data

I have an entity named messages with the following attributes:
-> message_id, message_from, message_to, message_body, date_created.
I want to fetch the last message for each user like we have in WhatsApp chat.
I found the following code for MySQL, but don't know how to use it with NSPredicates:
SELECT *
FROM messages m1
WHERE date_created = (SELECT MAX(m2.date_created)
FROM messages m2
WHERE m1.message_from = m2.message_from
AND m1.message_to = m2.message_to)
Could someone please help me to get the last message for each user from core data using NSPredicate?
First of all you need a method that extracts all Messages and all Users. For simplicity put it in your AppDelegate.m. Note that getAllMessages gets unique results by extracting data as a NSDictionary which by definition does not allow duplicate keys
-(NSArray*)getAllUsers
{
// initializing NSFetchRequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//Setting Entity to be Queried
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Message"
inManagedObjectContext:self.managedObjectContext];
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.returnsDistinctResults = YES;
[fetchRequest setEntity:entity];
[fetchRequest setPropertiesToFetch:#[#"userId"]];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"date_created" ascending:NO]];
NSError* error;
// Query on managedObjectContext With Generated fetchRequest
NSArray *fetchedRecords = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
// Returning Fetched Records
return fetchedRecords;
}
-(NSArray*)getAllMessages
{
// initializing NSFetchRequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//Setting Entity to be Queried
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Message"
inManagedObjectContext:self.managedObjectContext];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"date_created" ascending:NO]];
[fetchRequest setEntity:entity];
NSError* error;
// Query on managedObjectContext With Generated fetchRequest
NSArray *fetchedRecords = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
// Returning Fetched Records
return fetchedRecords;
}
Then you can extract and put the last message for each user in a NSMutableArray by doing this:
AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray * arrayUsers = [NSMutableArray arrayWithArray:[appDelegate getAllUsers]];
NSMutableArray * arrayMessages = [NSMutableArray arrayWithArray:[appDelegate getAllMessages]];
NSMutableArray * lastMessageForEachUser = [NSMutableArray array];//on this array you get the results
for (NSDictionary * dict in arrayUsers)
{
NSNumber * userId = [dict objectForKey:#"userId"];
NSPredicate * pr = [NSPredicate predicateWithFormat:#"userId == %#",userId];
NSArray * results = [arrayMessages filteredArrayUsingPredicate:pr];
Message * lastMessage = results.count>0 ? [results objectAtIndex:0] : nil;
[lastMessageForEachUser addObject:lastMessage];
}

NSPredicate creation with CoreData for an equal object

I have a strong pointer with the name "sensor" to an object of the class "SensorDB".
I tried to create a NSFetchedResultController so that my UIViewController gets notified, if something changes inside the "sensor" object.
To get the desired object into my NSFetchedResultController, I created following NSPredicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self == %#", self.sensor];
The goal is, that I have just one object in the NSFetchedResultController and get notifications about any updates, so that I can update my GUI.
But if I run my application, the app crashes when I try to alloc and init the NSFetchedResultController.
Here's my NSFetchedResultController code:
- (NSFetchedResultsController *)fetchedResultsControllerSensor
{
if (_fetchedResultsControllerSensor != nil) {
return _fetchedResultsControllerSensor;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SensorDB" inManagedObjectContext:dbHandler.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self == %#", self.sensor];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:dbHandler.managedObjectContext sectionNameKeyPath:nil cacheName:#"SensorCache"];
self.fetchedResultsControllerSensor = theFetchedResultsController;
_fetchedResultsControllerSensor.delegate = self;
return _fetchedResultsControllerSensor;
}
Thank you for your help or another/better approach
Linard
I thought, that I don't need a sort descriptor, because I've just one result object, but it looks like that the initialization of a NSFetchedResultController crashes, if there isn't at least one NSSortDescriptor.
That's now my NSPredicate and NSSortDescriptor:
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"(SELF = %#)", self.sensor];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"displayOrder" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

Core Data Programmatically Adding a to-many Relationship

I have an entity Item, and an entity Type (that has an attribute "Name") in a to-many relationship with Item. (Ie: Item: Brown Table, related to Type with Name "Coffee Table").
I've programmatically added new Items fine, using, for example:
[newItem setValue:([nameTextField stringValue]) forKey:#"Name"];
[newItem setValue:(costNumber) forKey:#"Cost"];
[newItem setValue:(priceNumber) forKey:#"Price"];
I've been searching for hours but can't find something that works for me adding a relationship to the new item. I'm using a NSPopUpButton to choose the Type of the item, and have tried methods like selectedItem, selectedTag, and selectedCell. I'm trying to get values from my "typeArray", which is filled as follows:
NSFetchRequest *fetchRequest2 = [[NSFetchRequest alloc] init];
NSEntityDescription *entity2 = [NSEntityDescription entityForName:#"Type"
inManagedObjectContext:managedObjectContext];
[fetchRequest2 setEntity:entity2];
NSError *error = nil;
typeArray = [managedObjectContext executeFetchRequest:fetchRequest2 error:&error];
if (typeArray == nil) {
NSLog(#"ERROR");
}
[fetchRequest2 release];
I'm not sure if the following is along the right lines:
NSManagedObject *selectedType = [typeArray objectAtIndex:[typePopUpButton selectedTag]];
But then I have no option for selectedType to add something like "addObject"..
Any help appreciated, thank you.
This is what I ended up using:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Type"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Name like %#", [typePopUpButton titleOfSelectedItem]];
[fetchRequest setPredicate:predicate];
NSArray *typeSet = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (typeSet == nil) {
NSLog(#"ERROR");
}
[fetchRequest release];
NSManagedObject *typeObject = [typeSet objectAtIndex:0];
[typeObject addItemsObject:newItem];
Basically, the object needs to be fetched so that a relationship can be made between the two items, and the predicate is based on the typePopUpButton's titleOfSelectedItem method.
I ensure to only select one object with the method [objectAtIndex:0].
It does bring up a warning though: NSManagedObject may not respond to 'addItemsObject'.
However this does work for me.

an NSPredicate for selecting a row with maximum value by group

I'm using Core Data to store entities in the form
TrackerEntry
username
timestamp
I want to select the latest record for each user. Using sql it would be something like
SELECT MAX(timestamp) FROM Log GROUP BY username
Is there anyway to create an NSPredicate to do this?
I would do it using NSExpression. This bit of code below won't work for you because you will have to group by username too, but it's a start for the best way of doing this without having to fetch everything. You want to perform the max and group in the db, not in memory - as it will be faster:
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
// Expression for the max
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"timestamp"];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName
inManagedObjectContext:self.coreDataStack.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setResultType:NSDictionaryResultType];
NSExpression *valueSumExpression = [NSExpression expressionForFunction:#"max:" arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[[NSExpressionDescription alloc] init] autorelease];
[expressionDescription setName:#"maxTimestamp"];
[expressionDescription setExpression:valueSumExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Filter
//NSPredicate *pred = [NSPredicate predicateWithFormat:#" your predicate here"];
//fetchRequest.predicate = pred;
NSArray *results = [self.coreDataStack.managedObjectContext executeFetchRequest:fetchRequest error:nil];
if([results count] == 0) {
} else {
// [[results objectAtIndex:0] valueForKey:#"maxTimestamp"];
}
This worked for me, but it is a loop. First you have to get the unique names into an array.
NSFetchRequest *nameRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Log" inManagedObjectContext:self.managedObjectContext];
nameRequest.entity = entity;
nameRequest.resultType = NSDictionaryResultType;
[nameRequest setPropertiesToFetch:[NSArray arrayWithObject:[[entity propertiesByName] objectForKey:#"name"]]];
NSArray *allNames = [self.managedObjectContext executeFetchRequest:nameRequest error:nil];
names = [allNames valueForKeyPath:#"#distinctUnionOfObjects.name"];
[nameRequest release];
NSLog(#"%#", names);
Not very efficient or convenient. After all, Core Data is not a database but an object graph.
Then you can loop through these and fetch just the top one.
NSMutableArray *mutableResults = [NSMutableArray array];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"Log" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *numberSort = [NSSortDescriptor sortDescriptorWithKey:#"number" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:numberSort];
request.fetchLimit = 1;
for (NSString *s in names) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"name = %#", s];
request.predicate = pred;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:nil];
[mutableResults addObject:[results objectAtIndex:0]];
}
[request release];
NSLog(#"%#", mutableResults);

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.