Getting the maximum value of a value in an Entity - objective-c

I'm trying to get the maximum value for an attribute in an Entity in core data. Apple has a nice example here of how to do this; however, it doesn't work for me. I have 10 objects in my moc and the following code always returns an array of size 0. Can anyone tell me what I am doing wrong? Thanks!
NSManagedObjectContext* moc = [self managedObjectContext];
// set the idx to the maximum value
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSEntityDescription* entity = [NSEntityDescription entityForName:#"Transaction"
inManagedObjectContext:moc];
[request setEntity:entity];
// Specify that the request should return dictionaries.
[request setResultType:NSDictionaryResultType];
// Create an expression for the key path.
NSExpression* keyPathExpression = [NSExpression expressionForKeyPath:#"idx"];
// Create an expression to represent the minimum value at the key path 'creationDate'
NSExpression* maxExpression = [NSExpression expressionForFunction:#"max:"
arguments:[NSArray arrayWithObject:keyPathExpression]];
// Create an expression description using the minExpression and returning a date.
NSExpressionDescription* expressionDescription = [[NSExpressionDescription alloc] init];
// The name is the key that will be used in the dictionary for the return value.
[expressionDescription setName:#"maxIdx"];
[expressionDescription setExpression:maxExpression];
[expressionDescription setExpressionResultType:NSInteger32AttributeType];
// Set the request's properties to fetch just the property represented by the expressions.
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Execute the fetch.
NSError* error = nil;
NSArray* objects = [moc executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error.
}
else {
if ([objects count] > 0) {
int newIdx = [[[objects objectAtIndex:0] valueForKey:#"maxIdx"] intValue] + 1;
[self setPrimitiveIdx:[NSNumber numberWithInt:newIdx]];
} else {
[self setPrimitiveIdx:[NSNumber numberWithInt:1]];
}
}

Your code looks right to me, so check the following
You actually have Transaction objects in your store. Just do a normal fetch from the same context.
Could you be doing this before you set up the context?
Check to see if there's anything in error -- just log [error localDescription]
Is NSInteger32AttributeType right for idx?
Is idx spelled correctly? Is Transaction? Meaning, they match your model.
PS: It won't matter for the result, but hopefully you have the releases from the code sample

Related

Core Data not updating, updated model

I have a very weird problem that has stumped me rather!
I have a core data entity that i have just added some new attributes to:
deleted - Boolean
deletedDate - Date
I have the following code, that upon pressing sets both those values on the core data object:
- (IBAction)deleteButtonInTable:(id)sender {
//Get the ID of the currently selected item in the table
NSInteger selected = [self.tweetTableView rowForView:sender];
//Create a predicate and fetch the objects from Core Data
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"approved == NO"];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"postDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];
[request setPredicate:testForTrue];
[request setSortDescriptors:sortDescriptors];
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:_managedObjectContext]];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:_managedObjectContext]];
//Assign the predicate to the fetch request
NSError *error = nil;
//Create an array from the returned objects
NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:request error:&error];
Tweet *selectedTweet = [fetchedObjects objectAtIndex:selected];
if (selectedTweet) {
selectedTweet.deleted = [NSNumber numberWithBool:TRUE];
selectedTweet.deletedDate = [NSDate date];
NSLog(#"%#",selectedTweet);
[self refreshTableView];
if (! self.tweetTableView){
NSLog(#"Tableview doesn't exist!!)");
}
[[self tweetTableView] reloadData];
[[self managedObjectContext] commitEditing];
[self saveAction:nil];
}
if ([self.autoWriteTweets isEqualToString:#"YES"]){
[self writeTweetsToXML];
[self saveAction:nil];
}
}
Now, if i watch the object in xcode with some breaks, i can see the attribute change on the object as i pass through the function, but i have an Table displaying a datasource, which is filtered to only show objects that have the deleted bool set to true, and nothing ever shows up there.
Now, to make things even more confusing i have a function that exports an array of the objects:
-(void)writeTweetsToXML{
//Create new fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//Set new predicate to only fetch tweets that have been favourited
NSPredicate *filterFavourite = [NSPredicate predicateWithFormat:#"approved == YES"];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:self.exportSort ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];
[request setSortDescriptors:sortDescriptors];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:_managedObjectContext]];
[request setResultType:NSDictionaryResultType];
//Assign the predicate to the fetch request
[request setPredicate:filterFavourite];
NSError *error = nil;
//Create an array from the returned objects
NSArray *tweetsToExport = [_managedObjectContext executeFetchRequest:request error:&error];
NSAssert2(tweetsToExport != nil && error == nil, #"Error fetching events: %#\n%#", [error localizedDescription], [error userInfo]);
//NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
//NSString *path = [NSString stringWithFormat:#"%#/tweets.xml", documents];
NSString *writeerror;
if(tweetsToExport) {
NSString * exportLocationFull = [[NSString alloc]initWithFormat:#"%#/tweets.xml",self.exportLocation];
BOOL success = [tweetsToExport writeToFile:exportLocationFull atomically:YES];
NSLog(#"Write Status = %d to %#", success, exportLocationFull);
}
else {
NSLog(#"%#",writeerror);
}
}
Now, when i look at the exported file, two things happen which are odd!
Firstly, an object that i have seen have it's deleted value set to true, exports with the value as 0.
Secondly, the deletedDate attribute does not export at all, every, despite it being in the core data model. I can't see any way this can happen as i am doing no specific filtering on the export.
It's like a getter/setter somewhere is broken, but i have checked the class files and everything is as it should be and set to #dynamic.
Any help would be greatly appreciated as i'm a bit lost as to what the hell is going on.
People had warned me about core data's quirks, but this is just plain odd!
Cheers
Gareth
Note 1
As an aside, i am using the exact same code from the first section to set other attributes on objects that are filtered and that seems to work fine!
You should not name an Core Data attribute "deleted", that conflicts with the
isDeleted method of NSManagedObject.
Compare https://stackoverflow.com/a/16003894/1187415 for a short analysis of that problem.
There are other attribute names that cause conflicts, e.g. "updated" (compare Cannot use a predicate that compares dates in Magical Record). Unfortunately, there are no warnings at compile time or runtime,
and the documentation on what acceptable attribute names are is also quite vague.
Things to check:
Did you save your core data entities with [managedObjectContext save:&error] at the appropriate places (e.g. before displaying the new table view data)? Did you check the error variable?
Did you migrate your model correctly with a new model version?
Are you reading the correct attributes and displaying them correctly (in UI or log statements)?
BTW, in your code you are setting the request entity twice.
Try saving the mananged object context before loading the table view.
The boolean deleted may be 0 before and not be changed or it may be auto-initialized (there is an field in the inspector to set default values) to 0. Date fields on the other hand are nil by default.
P.S. Use [NSNumber numberWithBoolean:YES] in Objective-C.

Core Data Issue - Unable to store NSNumber

I am trying to use Core Data in my application. I want to store an attribute "totalKM".
to store my attribute I use these lines:
Model *event = (Model *)[NSEntityDescription insertNewObjectForEntityForName:#"Model" inManagedObjectContext:managedObjectContext];
int number = 4;
[event setTotalKM:[NSNumber numberWithDouble:number]];
to read my attribute I use these:
Model *event = (Model *)[NSEntityDescription insertNewObjectForEntityForName:#"Model" inManagedObjectContext:managedObjectContext];
NSNumber *number = [event totalKM];
My attribute "totalKM" is always default value, O. And I have no exception. Am i missing something?
Thanks.
insertNewObjectForEntityForNamethis method use to put model to context.
this is a new object.
the right way is:
Set new one
Model *event = (Model *)[NSEntityDescription insertNewObjectForEntityForName:#"Model" inManagedObjectContext:managedObjectContext];
int number = 4;
[event setTotalKM:[NSNumber numberWithDouble:number]];
//save change if need
NSError *error = nil;
if (![self.manageObjectContext save:&error]) {
NIDERROR(#"save managedContext error :%#",error);
}
fetch object progress
When you need to get a model form coredate.u need 2 fetch use fetch
like this dome
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Board"
inManagedObjectContext:self.manageObjectContext]];
//set fetch conditions
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"board_id == %#", aBoardID];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *results = [self.manageObjectContext executeFetchRequest:fetchRequest error:&error];
if (error) {
NIDERROR(#"fetch board error.\n board id: %# \n error:%#", aBoardID, error);
return nil;
}
Model *model = results[0];
NSLog(#"%#",model.totalKM)

Core Data attribute uniqueness

Let's suppose that I download data from a remote web service. To be more precise I download a list of news from a web service, each news have a newsID that we can consider a primary key for the service so we won't find two equal newsID.
How can I can be sure that i insert only the data with a newsID that doesn't already exist?
I've draft my solution with this method:
- (BOOL)validateNewsID:(NSNumber **)newsID error:(NSError **)error
{
NSError *countError = nil;
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:[self entity]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"newsID == %#", *newsID];
[fetchRequest setPredicate:predicate];
NSUInteger resultCount = [SharedManagedObjectContext countForFetchRequest:fetchRequest error:&countError];
if (countError) {
OLog(countError);
*error = countError;
return NO;
}
if (resultCount > 0) {
NSString *errorString = #"NewsID should be unique";
NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorString
forKey:NSLocalizedDescriptionKey];
NSError *uniquenessError = [[[NSError alloc] initWithDomain:kNewsValidationDomain
code:kNewsValidationUniquenessCode
userInfo:userInfoDict] autorelease];
*error = uniquenessError;
return NO;
}
return YES;
}
but doesn't works as expect because, I suppose, when i perform the fetch request i find my same object previously insert in the context. Am I wrong?
How can i fix it?
Seems like you would find the same object you're trying to validate. Try one of these:
Execute the fetch request and remove the current object from the result array before checking for other entries.
Adjust the predicate so it excludes the current object, if possible.
Change resultCount > 0 to resultCount > 1.

An easy, reliable way to get max value of a core data object's attribute?

Is there an easy (or just reliable) way to find the max value of a core data attribute? Apple's example just does not work (plus it is ridiculously long and complicated for such a simple task). I have spent almost a day on this and haven't been able to find a satisfactory answer. Please help!
I get the same error as in this question: -[NSCFNumber count]: unrecognized selector. Like him, I haven't been able to find a solution to the problem.
This asker thinks he solved the same problem but, like someone else commented, the answer here is apparently wrong.
This question also has trouble with exactly the same code but at least the asker actually didn't get at an exception. It appears that he couldn't get it to work properly though and ended up using a different solution but didn't say what.
One person here got around it by retrieving results sorted and using the top value. Not ideal! However, if I cannot find a solution soon, I think I will have to do the same, or restructure my model or business rules or create and maintain a MaxValue class in my model to get around this...
I found this in the core data programing guide under fetching specific values. It refers to a minimum, but it answers your question. Not as easy as one would hope, but it isn't that hard to follow.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:context];
[request setEntity:entity];
// Specify that the request should return dictionaries.
[request setResultType:NSDictionaryResultType];
// Create an expression for the key path.
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"creationDate"];
// Create an expression to represent the minimum value at the key path 'creationDate'
NSExpression *minExpression = [NSExpression expressionForFunction:#"min:" arguments:[NSArray arrayWithObject:keyPathExpression]];
// Create an expression description using the minExpression and returning a date.
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
// The name is the key that will be used in the dictionary for the return value.
[expressionDescription setName:#"minDate"];
[expressionDescription setExpression:minExpression];
[expressionDescription setExpressionResultType:NSDateAttributeType];
// Set the request's properties to fetch just the property represented by the expressions.
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Execute the fetch.
NSError *error = nil;
NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error
}
else {
if ([objects count] > 0) {
NSLog(#"Minimum date: %#", [[objects objectAtIndex:0] valueForKey:#"minDate"]);
}
}
[expressionDescription release];
[request release];
have a NSManagedObject Subclass called TestEntity, which has one double property called tesetID.
-(double)getNextIndex
{
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"TestEntity" inManagedObjectContext:self.managedObjectContext]];
NSError * err = nil;
NSArray * array = [self.managedObjectContext executeFetchRequest:request error:&err];
NSNumber * value = [array valueForKeyPath:#"#max.testID"];
return [value doubleValue]+1;
}
-(void)test
{
for (int i = 0; i<25 ; i++) {
TestEntity * entity = [[TestEntity alloc] initWithEntity:[NSEntityDescription entityForName:#"TestEntity" inManagedObjectContext:self.managedObjectContext] insertIntoManagedObjectContext:self.managedObjectContext];
entity.testID = [NSNumber numberWithDouble:[self getNextIndex]];
NSLog(#"our testID is set as: %#",entity.testID);
}
}

UISlider core data programing

Im working on a simple "point based" app.
under settings the user set´s the number of points needed to get a "goodie" using a slider.
-(IBAction) sliderChanged: (id)sender {
UISlider *slider = (UISlider *) sender;
int progressAsInt =(int)(slider.value +0.5);
NSString *newText = [[NSString alloc] initWithFormat:#"%d",progressAsInt];
sliderLabel.text = newText;
[newText release];
this works fine, but how so i store the slider value in my core data model, and how do make my slider show the stored value when view loads.
hope u can help me out :-D
Hey gerry3 i found my error. i never set my toD-object in my settingsViewController, with:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription
entityForName:#"ToDo" inManagedObjectContext:_context]];
NSError *error = nil;
NSArray *array = [_context executeFetchRequest:request error:&error];
if (array == nil)
{
// Deal with error...
}
if(array.count > 0){
toDo = [array objectAtIndex:0];
} else { // no one to fetch - generate one
toDo = [NSEntityDescription
insertNewObjectForEntityForName:#"ToDo"
inManagedObjectContext:_context];
[toDo retain];
your code works like a charm .....
Thanks
Skov
The key here is that Core Data stores numeric attributes (e.g. integers, floats, etc) as NSNumber objects.
Say that your entity is called Record and it has a integer attribute called 'progress'.
If you create a managed object instance of Record named 'record', then you can set its progress like this:
[record setValue:[NSNumber numberWithInteger:progressAsInt] forKey:#"progress"];
When you want to update your view with the value from your model (usually in viewWillAppear:), you can get its progress like this:
NSNumber *progressNumber = [record valueForKey:#"progress"];
slider.value = [progressNumber floatValue];
Alternatively, if you generate the class files for the Record entity, you can just do:
record.progress = [NSNumber numberWithInteger:progressAsInt];
and:
slider.value = [record.progress floatValue];