Count number of entries from NSFetchRequest - iphone

I have a method which is void but I have to count the number of entries in it.
So is there any way I can do it??
I tried to implement it but its not working.
It's giving me a error at Nsfetch count.
The return is declared as null.
testcase1
{
//startdate and end date are same and both are 1 day before
NSTimeInterval secondsPerday=24*60*60;
NSDate *startdate = [[NSDate alloc]initWithTimeIntervalSinceNow:-secondsPerday];
NSDate *endate = [[NSDate alloc]initWithTimeIntervalSinceNow:-secondsPerday];
TravelerAppDelegate *delegate =[[UIApplication sharedApplication]delegate];
[delegate getWeatherforCity:#"#" state:#"#" country:#"#" startDate:startdate endDate:endate];
NSManagedObjectContext *allone=[delegate managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Weather" inManagedObjectContext:allone];
//WeatherXMLParser *delegate = [[WeatherXMLParser alloc] initWithCity:#"#" state:#"#" country:#"#"];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Weather" inManagedObjectContext:allone]];
[request setIncludesSubentities:NO];
NSError *err;
NSUInteger count = [allone countForFetchRequest:request error:&err];
if(count == NSNotFound) {
//Handle error
}
[request release];
return count;
}

First, you define the variable entity but when you set the entity of the fetch request, you are doing the same as above. Simply write:
[request setEntity:entity];
Then, NSFetchRequest has to have a sort descriptor array. So, add this:
[request setSortDescriptors:[NSArray array]];

If you want a method to return something, then set its return type.
-(void)testcase1
{
// Code goes here
}
Nothing (void) is returned. You'd call a method like this by:
[self testcase1];
To return something:
-(NSInteger)testcase1
{
// Code goes here
return count;
}
You would call such a method like this:
NSInteger count = [self testcase1];

Related

iOS - How can I sort the managed objects fetched from CoreData?

This is how i am inserting the data,
NSEntityDescription * entityDescription = [NSEntityDescription entityForName:[DOSnow entityDescription] inManagedObjectContext: proxy.managedObjectContext];
DOCurrentCondition *doSnow = [[[DOSnow alloc] initWithEntity:entityDescription insertIntoManagedObjectContext: proxy.managedObjectContext] autorelease];
NSXMLElement *snowConditionsElement = [[roseElement elementsForName:SNOW] lastObject];
NSArray *snowElements = [snowConditionsElement children];
for (NSXMLElement *snowElement in snowElements)
{
NSEntityDescription * entityDescription = [NSEntityDescription entityForName:[DOPair entityDescription] inManagedObjectContext: proxy.managedObjectContext];
DOPair *pair = [[[DOPair alloc] initWithEntity:entityDescription insertIntoManagedObjectContext: proxy.managedObjectContext] autorelease];
pair.key = [snowElement name];
pair.value = [snowElement stringValue];
[doSnow addConditionsObject: pair];
}
[proxy save];
And this is how i am fetching the data,
- (NSArray *) fetchSnowConditions
{
ApplicationFacade *appFacade = [ApplicationFacade appFacade];
NSManagedObjectContext *context = appFacade.rProxy.managedObjectContext;
NSFetchRequest * request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:[DOSnow entityDescription] inManagedObjectContext:context]];
NSError *error;
NSArray *result = [context executeFetchRequest:request error:&error];
return result;
}
So i am not getting the data in same order as i inserted.
You have to use [NSFetchRequest setSortDescriptor:] to get a meaningful ordering of your results. The NSFetchRequest documentation doesn't say anything about the default order of the results, so it's not a good idea to assume there is any.
Of course, in order to correctly specify the sort descriptor, you probably need to add a field to your managed objects to sort on, and assign a value to it when creating the objects. It could be an incrementing index field, a creation date, or something like that.

Core Data: does a fetch have to make a trip to persistent store?

Say I do this:
NSManagedObjectContext *context = #a managed object context";
NSString *entityName = #an entity name#;
NSFetchRequest *requestForAll = [NSFetchRequest requestWithEntityName:entityName];
NSArray *allObj = [context executeFetchRequest:requestForAll];
for (NSString *name in allNamesArray){
NSFetchRequest *requestForOne = [NSFetchRequest requestWithEntityName:entityName];
requestForOne.predicate = [NSPredicate predicateWithFormat:#"name == %#",name];
NSArray *ObjsWithName = [context executeFetchRequest:requestForOne];
#do some work with the obj#
}
Does the fetch in the loop incur a trip to the persistent store every time? Or those fetches will only be performed in coredata's row cache?
EDIT
I've written a fragment of testing code :
You need to create a core data entity named "Person" and it should have an attribute named "name", which is of type string.
use this code to populate some data:
self.array = #[#"alkjsdfkllaksjdf",#"asldjflkajdklsfjlk;aj",#"aflakjsdl;kfjalksdjfklajkldhkl;aj",#"aljdfkljalksdjfl;j" ,#"flajdl;kfjaklsdjflk;j",#"akldsjfklajdslkf",#"alkdjfkljaklsdjflkaj",#"alsdjflkajsdflj",#"adlkfjlkajsdfkljkla",#"alkdjfklajslkdfj"];
NSString *firstRunKey = #"oh its first run!";
NSString *firstRun = [[NSUserDefaults standardUserDefaults] objectForKey:firstRunKey];
if (!firstRun) {
for (NSString *name in self.array) {
Person *p = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
p.name = name;
}
}
[self.managedObjectContext save];
[[NSUserDefaults standardUserDefaults] setObject:firstRunKey forKey:firstRunKey];
[[NSUserDefaults standardUserDefaults] synchronize];
profile this two methods and you'll find usingCoreData costs much more time than usingFilterArray!
static int caseCount = 1000;
-(void)usingCoreData
{
NSLog(#"core data");
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Person"];
NSArray *allPersons = [self.managedObjectContext executeFetchRequest:request error:nil];
for (int i = 0; i < caseCount; i++){
for (NSString *name in self.array) {
request.predicate = [NSPredicate predicateWithFormat:#"name == %#",name];
NSArray *result = [self.managedObjectContext executeFetchRequest:request error:nil];
}
}
}
-(void)usingFilterArray
{
NSLog(#"filter array");
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Person"];
NSArray *allPersons = [self.managedObjectContext executeFetchRequest:request error:nil];
for (int i = 0; i < caseCount; i++){
for (NSString *name in self.array) {
NSArray *array = [allPersons filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"name == %#",name]];
}
}
}
Guess I need to answer my question myself.
I tested it and found, every time a fetch executed, core data will translate your NSFetchRequest into SQL command and invoke a data base query,the query result is firstly NSManagedObjectIDs, caching is applied to get the NSManagedObject from a NSManagedObjectID.
In conclusion, it caches object, but doesn't cache query result.
That means you execute the same NSFetchRequest for 10 times, it will query your persistent store for 10 times, event though you will get 10 times the same result. So in such situation, filtering array in memory will perform better than fetching.
The fetch will come from the specified cache when available.
EDIT:
Here's a link to a great tutorial that shows how to set up a NSFetchedResultsController that uses a cache.
http://www.raywenderlich.com/?p=999

Error: NSArray was mutated while being enumerated thrown when accessing globals and Core Data

I have this piece of code which I am using to update some values in Core Data when another object is added:
//Create new receipt
Receipt *receipt = [[Receipt alloc] init];
receipt.project = self.projectLabel.text;
receipt.amount = self.amountTextField.text;
receipt.descriptionNote = self.descriptionTextField.text;
receipt.business = self.businessNameTextField.text;
receipt.date = self.dateLabel.text;
receipt.category = self.categoryLabel.text;
receipt.paidBy = self.paidByLabel.text;
receipt.receiptImage1 = self.receiptImage1;
//Need to set this to 2
receipt.receiptImage2 = self.receiptImage1;
receipt.receiptNumber = #"99";
int count = 0;
int catCount = 0;
for (Project *p in appDelegate.projects)
{
if ([p.projectName isEqualToString:receipt.project]){
double tempValue = [p.totalValue doubleValue];
tempValue += [receipt.amount doubleValue];
NSString *newTotalValue = [NSString stringWithFormat:#"%.02f", tempValue];
NSString *newProjectName = p.projectName;
//remove entity from Core Data
NSFetchRequest * allProjects = [[NSFetchRequest alloc] init];
[allProjects setEntity:[NSEntityDescription entityForName:#"Project" inManagedObjectContext:appDelegate.managedObjectContext]];
[allProjects setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError * error = nil;
NSArray * projectsArray = [appDelegate.managedObjectContext executeFetchRequest:allProjects error:&error];
//Delete product from Core Data
[appDelegate.managedObjectContext deleteObject:[projectsArray objectAtIndex:count]];
NSError *saveError = nil;
[appDelegate.managedObjectContext save:&saveError];
[appDelegate.projects removeObjectAtIndex:count];
NSLog(#"Removed project from Core Data");
//Insert a new object of type ProductInfo into Core Data
NSManagedObject *projectInfo = [NSEntityDescription
insertNewObjectForEntityForName:#"Project"
inManagedObjectContext:appDelegate.managedObjectContext];
//Set receipt entities values
[projectInfo setValue:newProjectName forKey:#"name"];
[projectInfo setValue:newTotalValue forKey:#"totalValue"];
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSLog(#"Added Project to Core Data");
Project *tempProject = [[Project alloc] init];
tempProject.projectName = [projectInfo valueForKey:#"name"];
tempProject.totalValue = [projectInfo valueForKey:#"totalValue"];
[appDelegate.projects addObject:tempProject];
}
count++;
}
for (Category *c in appDelegate.categories){
if ([c.categoryName isEqualToString:receipt.category]){
double tempValue = [c.totalValue doubleValue];
tempValue += [receipt.amount doubleValue];
NSString *newTotalValue = [NSString stringWithFormat:#"%.02f", tempValue];
NSString *newCategoryName = c.categoryName;
//remove entity from Core Data
NSFetchRequest * allCategories = [[NSFetchRequest alloc] init];
[allCategories setEntity:[NSEntityDescription entityForName:#"Category" inManagedObjectContext:appDelegate.managedObjectContext]];
[allCategories setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError * categoriesError = nil;
NSArray * categoriesArray = [appDelegate.managedObjectContext executeFetchRequest:allCategories error:&categoriesError];
//Delete product from Core Data
[appDelegate.managedObjectContext deleteObject:[categoriesArray objectAtIndex:catCount]];
NSError *categorySaveError = nil;
[appDelegate.managedObjectContext save:&categorySaveError];
[appDelegate.categories removeObjectAtIndex:catCount];
NSLog(#"Removed category from Core Data");
NSError * error = nil;
//Insert a new object of type ProductInfo into Core Data
NSManagedObject *categoryInfo = [NSEntityDescription
insertNewObjectForEntityForName:#"Category"
inManagedObjectContext:appDelegate.managedObjectContext];
//Set receipt entities values
[categoryInfo setValue:newCategoryName forKey:#"name"];
[categoryInfo setValue:newTotalValue forKey:#"totalValue"];
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSLog(#"Added Category to Core Data");
Category *tempCategory = [[Category alloc] init];
tempCategory.categoryName = [categoryInfo valueForKey:#"name"];
tempCategory.totalValue = [categoryInfo valueForKey:#"totalValue"];
[appDelegate.categories addObject:tempCategory];
}
catCount++;
}
This code gives the error:
'...was mutated while being enumerated'.
Can anyone explain why? Also, is there a better approach to doing what I'm trying to achieve?
The error you're seeing is accurate. The problem is that you're mutating (changing) a collection while iterating over it. Basically, you're doing something of the form:
for (Project *p in appDelegate.projects) {
...
[p addObject: X]
}
This isn't allowed.
One simple solution is to make a new collection of the objects you want to add, and then add them to the original container outside of your loop. Something like:
NSMutableArray *array = [NSMutableArray array];
for (Project *p in appDelegate.projects) {
...
[array addObject:X];
}
[p addObjects:array];
By the way, did you google for the error text "was mutated while being enumerated"? I'd be surprised if you didn't find the answer for this common problem just by googling.
Also, when posting an error message, it's helpful to post the full line, not just part of it.
You are adding and removing items from appDelegate.projects while iterating over it in a for-each loop here:
[appDelegate.projects removeObjectAtIndex:count];
// ...
[appDelegate.projects addObject:tempProject];
And the same for appDelegate.categories:
[appDelegate.categories removeObjectAtIndex:count];
// ...
[appDelegate.categories addObject:tempProject];
AFAIK, in such case you should use a simple for loop, and access the array with index.

get date from an nsdictionary and match them

I am using nsdictionary to store dates.I am trying to give certain dates from my testcase and get the dates from the dictionary.Say ,my dictionary has 10 dates.I wish to match 3 dates and get only the 3 dates from the dictionary.I am unable to do that.Can anyone tell me how I can do that?I am getting all the 10 dates even I try to get the 3 dates.well,heres my codeif(([dd1 laterDate:startDate] || [dd1 isEqualToDate:startDate]) &&
([dd1 earlierDate:endDate] || [dd1 isEqualToDate:endDate] ) )
{
if ([startDate earlierDate:dd1] && [endDate earlierDate:dd1])
{
//dd==startDate;
NSManagedObject *allnew = Nil;
NSManagedObjectContext *allone=[self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Weather" inManagedObjectContext:allone];
NSLog(#" The NEW ENTITY IS ALLOCATED AS entity is %#",entity);
WeatherXMLParser *delegate = [[WeatherXMLParser alloc] initWithCity:city state:state country:country];
NSXMLParser *locationParser = [[NSXMLParser alloc] initWithContentsOfURL:delegate.url];
[locationParser setDelegate:delegate];
[locationParser setShouldResolveExternalEntities:YES];
[locationParser parse];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
predicate = [NSPredicate predicateWithFormat:
#"city == %# and state == %# and country == %# and date==%# and date==%#", city, state, country,startDate,endDate];
[request setPredicate:predicate];
NSError *err;
NSUInteger count = [allone countForFetchRequest:request error:&err];
NSLog(#" minimum salary is %#",predicate);
// If a predicate was passed, pass it to the query
if(predicate !=NULL){
//[self deleteobject];
}
Weather *weather = (Weather *)[NSEntityDescription insertNewObjectForEntityForName:#"Weather"
inManagedObjectContext:self.managedObjectContext];
weather.date = [fields objectForKey:#"date"];
weather.high =[fields objectForKey:#"high"];
weather.low = [fields objectForKey:#"low"];
weather.city =[fields objectForKey:#"city"];
weather.state =[fields objectForKey:#"state"];
weather.country =[fields objectForKey:#"country"];
NSString*icon=[fields objectForKey:#"icon"];
NSString *all=[icon lastPathComponent];
weather.condition = all;
[self saveContext];
I wish to get only 2 dates but I am getting all 4 elements from nsdictionary.I am passing my startdate and enddate from the testcase and I am getting dates from google weather api using nsxmlparser and storing them in nsdictionary.I am getting the first date and incrementing each date and storing them.My NSdictionary looks likegot {
city = #;
condition = "Mostly Sunny";
country = #;
date = "2011-08-11 05:00:00 +0000";
"day_of_week" = Thu;
high = 81;
icon = "/ig/images/weather/mostly_sunny.gif";
low = 65;
startdate = "2011-08-11 05:00:00 +0000";
state = #;
int count = 0;// global
-(void)threeDateMatches(NSDate*)testCaseDate{
for(NSDate* d1 in dateDictionary){
if( [[dateComparisonFormatter stringFromDate:testCaseDate] isEqualToString: [dateComparisonFormatter stringFromDate:d1]] ) {
count = count+1;
//save your d1 in another variable and that will give you three matches date
//with your testCaseDate
}
if(count>3){
break;
}
}
I have just given you an example, did not test it by running it.

Core Data saves object even when validation fails

I've included the function that is handling all of the save functionality.
Here's my problem.
I am grabbing 5 input values and saving it as a CoreData Log Entity.
Even when the Log object fails to validate, it is still being saved when I back out of the form and look at the table view.
How can I force Core Data to only save the object once it's validated?
-(void) saveLog {
NSManagedObjectContext *managedObjectContext = [(AppDelegate_Shared *)[[UIApplication sharedApplication] delegate] managedObjectContext];
FormPickerCell *bloodPressure = (FormPickerCell *) [self.formController fieldAsObject:#"bloodpressure"];
NSInteger systolic = [(PressureDataSource*)bloodPressure.pickerCellDelegate selectedSystolicPressureForFormPickerCell:bloodPressure];
NSInteger diastolic = [(PressureDataSource*)bloodPressure.pickerCellDelegate selectedDiastolicPressureForFormPickerCell:bloodPressure];
NSLog(#"bp is %d / %d", systolic, diastolic);
NSLog(#"date is %#", [self.formController valueForField:#"date"]);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss ZZZ"];
if (self.isNewLog && !self.validationHasFailed) {
self.log = [NSEntityDescription
insertNewObjectForEntityForName:#"Log" inManagedObjectContext:managedObjectContext];
}
NSString *heartRate = [[self.formController valueForField:#"heartrate"] stringByReplacingOccurrencesOfString:#" bpm" withString:#""];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
self.log.created = [NSDate date];
self.log.notes = [self.formController valueForField:#"notes"];
self.log.systolic = [NSNumber numberWithInteger:systolic];
self.log.diastolic = [NSNumber numberWithInteger:diastolic];
self.log.stressLevel = [self.formController valueForField:#"stresslevel"];
self.log.logDate = [dateFormatter dateFromString:[self.formController valueForField:#"date"]];
self.log.heartrate = [f numberFromString:heartRate];
NSLog(#"Log date is %#",[self.formController valueForField:#"date"]);
[f release];
NSError *error;
NSString *title;
NSString *growlDescription;
if ([self.log validateForInsert:&error]){
NSLog(#"after validation returned true");
if(![managedObjectContext save:&error]) {
NSLog(#"Unresolved error");
title = #"Error Occurred";
growlDescription = [error localizedDescription];
self.validationHasFailed = YES;
} else {
title = #"Log Saved!";
growlDescription = #"Log saved successfully";
[self.navigationController popViewControllerAnimated:YES];
}
} else {
NSLog(#"after validation returned false");
NSLog(#"Unresolved error");
title = #"Error Occurred";
growlDescription = [error localizedDescription];
self.validationHasFailed = YES;
}
IZGrowlNotification *notification = [[IZGrowlNotification alloc] initWithTitle:title
description:growlDescription
image:nil
context:nil
delegate:self];
[[IZGrowlManager sharedManager] postNotification:notification];
[notification release];
error = nil;
}
This is a bit late but I just saw your question so figured I'd toss an answer at you. Any object you add to the managed object context will be saved whenever you next save. You could leave your code as is and just delete the new object with [managedObjectContext deleteObject:self.log] but a better method is below.
Your code:
self.log = [NSEntityDescription insertNewObjectForEntityForName:#"Log" inManagedObjectContext:managedObjectContext];
Creates a new Log instance and inserts into the managed object context. What you want to do instead is:
self.log = [[Log alloc] initWithEntity:[NSEntityDescription entityForName:#"Log" inManagedObjectContext:managedObjectContext] insertIntoManagedObjectContext:nil];
This will create a new 'Log' instance that has not yet been inserted into the MOC. If validation succeeds, before you save the MOC you insert the self.log as follows:
[managedObjectContext insertObject:self.log];
Then you save. If validation fails, don't insert the object and you're good to go.