CoreData compare two NSManagedObjects by their attributes and relationships - iphone

I have filter object. It has several attributes and relationships. I want to create new filter object if there's no one with the same attributes & relationships. How to accomplish this ?

I would choose a more general approach..
I would take the NSEntityDescriptions of the object and build a predicate with all properties in that Description.
so like..
- (void)insertIfUnique:(NSManagedObject*)obj inContext:(NSManagedObjectContext*)ctx {
NSMutableString *format = [NSMutableString string];
NSEntityDescription *desc = obj.entity;
NSArray *attrs = desc.attributeKeys;
for(NSString *attr in attrs) {
if(format.length)
[format appendString:#" AND "];
[format appendFormat:#"%#==%#", attr, [obj valueForKey:attr]];
}
NSPredicate *p = [NSPredicate predicateWithFormat:format];
NSFetchRequest *f = [[NSFetchRequest alloc] initWithEntityName:desc.name];
f.predicate = p;
if([ctx countForFetchRequest:f error:nil]==0)
[ctx insertObject:obj];
}

You have to manually search CoreData for any existing filter objects. If not found, you can process to create a new filter:
Here is one helper function
+(id)uniqueEntityfForName:(NSString *)name
withValue:(id)value
forKey:(NSString *)key
inManagedObjectContext:(NSManagedObjectContext *)context {
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
request.entity = [NSEntityDescription entityForName:name inManagedObjectContext:context];
request.predicate = [NSPredicate predicateWithFormat:[key stringByAppendingString:#" == %#"], value];
NSArray *result = [context executeFetchRequest:request error:nil];
id entity = [result lastObject];
if (entity == nil) {
entity = [NSEntityDescription insertNewObjectForEntityForName:name inManagedObjectContext:context];
[entity setValue:value forKey:key];
} else {
entity = [result lastObject];
}
return entity;
}
I use this method like this:
SomeEntity *entity = [CDUtils uniqueEntityfForName:#"SomeEntity" withValue:#"foo" forKey:#"bar" inManagedObjectContext:context];
You may have to define your own predicate.

Related

update an attribute using core data

I have an entity that has 3 attributes: firstname, lastname and score. i am able to search the score of a person by firstname and last name and display it on an UILabel (so far so good) my problem is that i don't know how to update the score attribute with a new value. below the code that i use to perform the search and display the old value.
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Scorecard" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"firstname like %# and lastname like %#", self.firstnameTextField.text, self.lastnameTextField.text];
[request setPredicate:predicate];
NSError *error;
NSArray *integer = [context executeFetchRequest:request error:&error];
if(integer.count <= 0){
self.displayLabel.text = #"No records found";
}
else {
NSNumber *score;
for (NSManagedObject *object in integer) {
score = [object valueForKey:#"score"];
int x = [score intValue];
// New Value is going to be the old value + 2
int y = x + 2;
//displays old value found
[self.displayLabel setText:[NSString stringWithFormat:#"old value %d",x]];
}
}
update #1
i was able to update the attribute and save it however i had to convert my new Integer value to a string and then convert it back to an integer. i don't like the extra step. if anyone has another suggestion please let me know and thanks for the help. below the updated code.
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Scorecard" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"firstname like %# and lastname like %#", self.firstnameTextField.text, self.lastnameTextField.text];
[request setPredicate:predicate];
NSError *error;
NSArray *integer = [context executeFetchRequest:request error:&error];
if(integer.count <= 0){
self.displayLabel.text = #"No records found";
}
else {
NSNumber *score;
for (NSManagedObject *object in integer) {
score = [object valueForKey:#"score"];
int x = [score intValue];
int y = x + 2;
NSString* z = [NSString stringWithFormat:#"%d",y];
[object setValue:[NSNumber numberWithInteger:[z integerValue]] forKey:#"score"];
[self.displayLabel setText:[NSString stringWithFormat:#"Value updated to %d",y]];
[context save:&error];
}
}
update 2
Thanks to Kevin's answer I was able to solve my problem. I am now able to replace an integer value from an attribute on my core data with another integer value. below my code finally working.
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Scorecard" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"firstname like %# and lastname like %#", self.firstnameTextField.text, self.lastnameTextField.text];
[request setPredicate:predicate];
NSError *error;
NSArray *integer = [context executeFetchRequest:request error:&error];
if(integer.count <= 0){
self.displayLabel.text = #"No records found";
}
else {
NSNumber *score;
for (NSManagedObject *object in integer) {
score = [object valueForKey:#"score"];
int x = [score intValue];
int y = x + 2;
[object setValue:[NSNumber numberWithInt:y] forKey:#"score"];
[self.displayLabel setText:[NSString stringWithFormat:#"Value updated to %d",y]];
[context save:&error];
}
}
Get an object like you did before then set's it's score property
[object setValue:[NSNumber numberWithInt:y] forKey:#"score"];
My first suggestion would be to use NSManagedObject's subclasses instead of using primitives. You can do this by using the core data inspector and changing your entity class.
Now, to do a change to the score, you need to find the object you need to change, and assign the value. Once the value is assigned, you will need to save the NSManagedObject the object belongs to. Once the context successfully saves, your changes are persistent.

Database update using Core Data

I am trying to update my local database when app gets response from the web server. When app gets the update from web server, I fetch the data from the local database by matching the id with the response and get one row and perform update code but local database does not get updated and also does not give an error.
What should be the solution???
-(void)checkID:(NSMutableDictionary *)dict
{
NSDictionary *dictEvent = [dict objectForKey:#"Event"];
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *selectedManagedObject = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Events" inManagedObjectContext:context];
NSSortDescriptor *sortDescObj = [[NSSortDescriptor alloc] initWithKey:#"event_id" ascending:YES];
NSError *error = nil;
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"user_id=%# and event_id=%#",[NSNumber numberWithInt:[[dictEvent valueForKey:#"user_id"] intValue]],[NSNumber numberWithInt:[[dictEvent valueForKey:#"id"] intValue]]]];
NSLog(#"Predicate = %#",predicate);
NSArray *arrSortDescriptors = [NSArray arrayWithObject:sortDescObj];
[fetchRequest setSortDescriptors:arrSortDescriptors];
[fetchRequest setEntity:entity];
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setPredicate:predicate];
NSArray *arrResult = [context executeFetchRequest:fetchRequest error:&error];
if ([arrResult count]>0)
{
NSArray *arrKey = [dictEvent allKeys];
NSArray *arrValue = [dictEvent allValues];
NSLog(#"ArrKey : %#\nArrValue : %#",arrKey,arrValue);
selectedManagedObject = [arrResult objectAtIndex:0];
for(int i = 0; i < [arrKey count] ; i++)
{
NSLog(#"selectedMng :- %#",selectedManagedObject);
NSLog(#"KEY: %#\t: %#",[arrKey objectAtIndex:i],[arrValue objectAtIndex:i]);
if ([[arrKey objectAtIndex:i]isEqualToString:#"id"])
{
[selectedManagedObject setValue:[arrValue objectAtIndex:i] forKey:#"event_id"];
}
else if([[arrKey objectAtIndex:i]isEqualToString:#"invited_status"])
{
[selectedManagedObject setValue:[arrValue objectAtIndex:i] forKey:#"invite_status"];
}
else
{
[selectedManagedObject setValue:[arrValue objectAtIndex:i] forKey:[arrKey objectAtIndex:i]];
}
}
if (! [selectedManagedObject.managedObjectContext save:&error])
{
NSLog(#"updateEntityIntoDataBaseNamed - Error :: %#", [error localizedDescription]);
}
// }
}
}
Besides modifying your predicate as suggested by #Martin
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"user_id=%# && event_id=%#",
[NSNumber numberWithInt:[[dictEvent valueForKey:#"user_id"] intValue]],
[NSNumber numberWithInt:[[dictEvent valueForKey:#"id"] intValue]]
];
note that in two cases, you are updating your object using non matching keys: this happens for id and event_id, and for invited_status and invite_status.
You cannot use stringWithFormat within predicateWithFormat. Your predicate should probably look like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"user_id=%# and event_id=%#",
[NSNumber numberWithInt:[[dictEvent valueForKey:#"user_id"] intValue]],
[NSNumber numberWithInt:[[dictEvent valueForKey:#"id"] intValue]]
];

Only one value is being written into Core Data. Why ?

I am a newbie and I would like to store and retrieve data from a Core Data database.
I get all the data from a php file, which communicates with a SQL database. This php file returns a JSON object, which in turn is parsed by my app and then written into Core Data by doing the following:
AppDelegate.m
-(void)writeDataIntoCoreData
{
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *articles = [NSEntityDescription
insertNewObjectForEntityForName:#"Articles"
inManagedObjectContext:context];
for(int i= 0; i<[json count];i++){
NSDictionary *info = [json objectAtIndex:i];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber * myNumber = [f numberFromString:[info objectForKey:#"userID"]];
[articles setValue:myNumber forKey:#"articleID"];
[articles setValue:[info objectForKey:#"username"] forKey:#"author"];
[articles setValue:[info objectForKey:#"user_pic"] forKey:#"text"];
NSLog(#"Name = %#",[info objectForKey:#"username"]);
NSLog(#"Text = %#",[info objectForKey:#"user_pic"]);
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
MainViewController.m
Here I try to retrieve all the data in the Core Data database by iterating through the fetchedObjects.
-(IBAction)showCD:(id)sender
{
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Articles" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
NSLog(#"id: %#", [info valueForKey:#"articleID"]);
NSLog(#"Name: %#", [info valueForKey:#"author"]);
NSLog(#"Text: %#", [info valueForKey:#"text"]);
}
}
The NSLog, however, only displays one object, namely the first one that was written into the database. What is wrong ?
move your
NSManagedObject *articles = [NSEntityDescription
insertNewObjectForEntityForName:#"Articles"
inManagedObjectContext:context];
into your for loop,
for(int i= 0; i<[json count];i++){
NSManagedObject *articles = [NSEntityDescription
insertNewObjectForEntityForName:#"Articles"
inManagedObjectContext:context];
NSDictionary *info = [json objectAtIndex:i];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber * myNumber = [f numberFromString:[info objectForKey:#"userID"]];
[articles setValue:myNumber forKey:#"articleID"];
[articles setValue:[info objectForKey:#"username"] forKey:#"author"];
[articles setValue:[info objectForKey:#"user_pic"] forKey:#"text"];
NSLog(#"Name = %#",[info objectForKey:#"username"]);
NSLog(#"Text = %#",[info objectForKey:#"user_pic"]);
}
Move the call to insertNewObjectForEntityForName:inManagedObjectContext: inside your loop. The way you're doing it now, you're creating just one managed object and then repeatedly change its attributes.

Core Data iPhone - Loading a String

I am having troubles with understanding how to load data with Core Data. This is what I have:
Entity named 'People'
Attributes of this entity named 'firstName' (string), 'lastName' (string), 'isSpecial' (boolean), 'isVerySpecial' (boolean).
I want to get the first and last name of any person who is special or very special so that I can then put those names into labels and wherever I like as I wish. I have played about with fetch requests without luck.
Any help would be much appreciated, thanks.
Something like this should do it:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *weightEntity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:[[yourCoreDataManager sharedInstance] managedObjectContext]];
[fetchRequest setEntity:weightEntity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"isSpecial == %# OR isVerySpecial == %#", [NSNumber numberWithBool:YES], [NSNumber numberWithBool:YES]]];
NSError *error = nil;
NSArray *result = [[yourCoreDataManager sharedInstance] managedObjectContext] executeFetchRequest:fetchRequest error:&error];
And then:
Person *person = [result objectAtIndex:0];
NSString *lastName = person.lastName;
NSString *firstName = person.firstName;
Something like that should do the trick:
self.fetchedResultsController = [Person fetchAllSortedBy:#"lastName" ascending:YES withPredicate:[NSPredicate predicateWithFormat:#"isSpecial == %# OR isVerySpecial == %#", [NSNumber numberWithBool:YES], [NSNumber numberWithBool:YES]] groupBy:nil delegate:self];
then you will get an array of persons in your fetchedResultsController. To get it simple use
NSMutableArray *yourArray = [NSMutableArray arrayWithArray: self.fetchedResultsController.fetchedObjects];
Hope it helps

Get count of objects with specific relationship

I have to entity, A and B (A <<---> B) so an A object can have one B object and a B object can have more than one A object (Correct me if I'm wrong, I'm new to core data relationships).
Now, I want to know how many A objects are in a B object. For example I have a shop (employee <<---> shop) and I want to know how many employee work in that shop.
I've create something like this without success:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Task" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"project == %#", aProject];
[request setPredicate:predicate];
NSError *error = nil;
NSInteger resultNumber = [self.managedObjectContext countForFetchRequest:request error:&error];
if (!resultNumber) {
NSLog(#"Risultati della richiesta nulli!");
abort();
}
NSLog(#"getCountOfProjects called");
NSLog(#"Results: %#", resultNumber);
return resultNumber;
Can you help me? Than you so much! ;)
Update
I've tried to use this code but it doesn't work...
-(void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Project *projects = (Project *)[fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = projects.title;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%# entries", [projects.task count]];
}
Why not just say something like [[myShop employees] count]?