How do NSEntityDescription and NSAttributeDescription work? - core-data

I am trying to understand how these things work : NSEntityDescription, NSAttributeDescription, attributeType.
I think these few lines of code work, since I get what I expect for the value of X.
Can some one tell me how I should modify the inner part of the loop, that is the line : X++;
in order to get the names and type of the properties in the entity : "myentity" ?
//::::::::::::::::::::::::::: EXPERIMENT
MeDaSyAGAppDelegate *TheAppDelegate=[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *TheContext=[TheAppDelegate managedObjectContext];
NSEntityDescription *TheEntityDesc;
TheEntityDesc=[NSEntityDescription entityForName:#"myentity" inManagedObjectContext:TheContext];
int X=0;
NSDictionary *attribs=[TheEntityDesc attributesByName];
for (NSAttributeDescription *eachA in [attribs allValues]) {
X++;
}
[self showMessageBox:[NSString stringWithFormat:#"X = %d.",X]];
//::::::::::::::::::::::::::: EXPERIMENT

First: Format your code. See below.
Second: try to do this:
NSLog(#"%#",eachA.name);//The name
NSLog(#"%d",[eachA attributeType])//The type, this is an integer
NSLog(#"%#",[eachA attributeValueClassName]);//Class name receiver
See:NSAttributeDescription Class Docs
Formatting: This looks better. (Attributes start with lower cases and use spaces)
//::::::::::::::::::::::::::: EXPERIMENT
MeDaSyAGAppDelegate *theAppDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *theContext = [TheAppDelegate managedObjectContext];
NSEntityDescription *theEntityDesc = [NSEntityDescription entityForName:#"myentity" inManagedObjectContext:TheContext];
int X = 0;
NSDictionary *attribs = [theEntityDesc attributesByName];
for (NSAttributeDescription *eachA in [attribs allValues]) {
X++;
}
[self showMessageBox:[NSString stringWithFormat:#"X = %d.", X]];
//::::::::::::::::::::::::::: EXPERIMENT

Related

Saving Core Data related data and retrieving with NSPredicate and NSFetchedResultsController with multiple entities

I'm fairly new to Core Data and am still trying to understand accessing and filtering related data. My problem is either I'm not getting the data correctly into the managedObjectContext or I'm not pulling it out correctly. (I think the first, but I'm not sure.)
Here's my data model with two entities related one to many: (I plan to refactor once I get one level of relationship working.)
I have a SeasonsVC in which you click on a season name and the list of games for that season is supposed to appear in the GamesVC and you have the option to add or edit an existing game. This works fine at a first pass. I can add and edit games via this code in the GameDetailsVC:
-(IBAction)done:(UIBarButtonItem *)sender {
Game *game = nil;
if (self.gameToEdit != nil) {
game = self.gameToEdit;
NSLog(#"Hitting gametoedit");
} else {
game = [NSEntityDescription insertNewObjectForEntityForName:#"Game" inManagedObjectContext:self.managedObjectContext];
NSLog(#"Hitting new game");
}
game.opponent = self.opponentTextField.text;
//season.seasonDescription = self.seasonDescriptionTextView.text;
NSLog(#"Game to edit: %#", game.opponent);
//NSLog(#"Season: %#", season);
//NSLog(#"MOC in done: %#", self.managedObjectContext);
//NSLog(#"Season name: %#", season.seasonName);
[self dismissViewControllerAnimated:YES completion:nil];
}
I can then see the games in the GamesVC via the fetchedResultsController and delegate methods, but each game is associated with every season. Once I try to filter the data with a predicate so that I only see the games that were added for that season all the games disappear. Here's the code for that from the GamesVC:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Game" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSLog(#"Season name for predicate %#", self.season.seasonName);//shows correct season name
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"season.seasonName like '%#'", self.season.seasonName]];
[fetchRequest setPredicate: predicate];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"dateOfGame" ascending:NO] ;
[fetchRequest setSortDescriptors:#[sortDescriptor]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil
cacheName:#"Root2"];
self.fetchedResultsController = theFetchedResultsController;
self.fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Since I can log the correct season name right before the predicate statement, I think that the added games are not getting "associated" with the correct season when I put them in the MOC in the done: method shown above; otherwise, I'd expect the predicate to find them.
Can you help this rookie? Thanks.
A day later I have this sorted. I needed to make three changes to what I was doing.
First, in my prepareForSegue in my GameListVC I needed to pass the season along to the GameDetailsVC by changing the code to this. (Just added the one line: controller.season = self.season)
//pass the Season object to be edited
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
Game *gameToEdit = [self.fetchedResultsController objectAtIndexPath:indexPath];
controller.gameToEdit = gameToEdit;
controller.season = self.season;
Second, I had to put the new/edited game object into the MOC by adding these two lines to my done: method in the GameDetailsVC.
NSMutableSet *games = [self.season mutableSetValueForKey:#"games"];
[games addObject:game];
Third, I needed to simplify my predicate statement in my fetchedResultsController method in the GameListVC.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"season.seasonName like %#", self.season.seasonName];
So, to answer my own question, I was both not saving the game linked to the season and my predicate was wrong.
I hope some of this helps another rookie.

Core Data one to many record insert error

I have three entities
Forms{
name:string
jobs<-->>JSAjobs.form
}
JSAjobs{
name:string
form<<-->Forms.jobs
}
Jobs{
step:string
jobs<<-->Forms.jobs
}
I am getting this error:
to-many relationship fault "jobs" for objectID 0x95afe60. . . fulfilled from database. Got 0 rows
Now I save the row for Forms entity first later on I need to fetch the last record on the Form entity create a new row on JSAjobs with details on JSAjop like next
Thanks
NSMutableArray *jobData = [[NSMutableArray alloc]initWithArray:controller.jobData];
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"JSAform" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *testForFalse = [NSPredicate predicateWithFormat:#"emailed == NO"];
[fetchRequest setPredicate:testForFalse];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
NSLog(#"Fetched Rows: %i", [fetchedObjects count]);
//NSManagedObject *existingParent= //... results of a fetch
JSAform *lastForm = [fetchedObjects objectAtIndex:0];
JSAjobs *newJobs = [NSEntityDescription insertNewObjectForEntityForName:#"JSAjobs" inManagedObjectContext:context];
// Setting new values
newJobs.jobType = [NSString stringWithFormat:#"%#", [jobData objectAtIndex:0]];
newJobs.jobName = [NSString stringWithFormat:#"%#", [[[jobData objectAtIndex:1]objectAtIndex:0] objectAtIndex:0]];
newJobs.comments = [NSString stringWithFormat:#"%#", [[[jobData objectAtIndex:1]objectAtIndex:0] objectAtIndex:1]];
newJobs.date = [NSDate date];
[newJobs setValue:lastForm forKey:#"form"];
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
//New SOP Value
JOBsop *jobSOP = [NSEntityDescription insertNewObjectForEntityForName:#"JOBsop" inManagedObjectContext:context];
for (int i = 0; i< [[jobData objectAtIndex:1]count]; i++){
NSLog(#"Value for key: %i", i);
if (i > 0){
for (int k = 0; k< [[[jobData objectAtIndex:1]objectAtIndex:i] count]; k++){
jobSOP.step = [[[jobData objectAtIndex:1]objectAtIndex:i] objectAtIndex:k];
[jobSOP setValue:newJobs forKey:#"jobs"];
// [NSNumber numberWithInt:[txtBoots.text integerValue]];
NSLog(#"Simple key: %#", [[[jobData objectAtIndex:1]objectAtIndex:i] objectAtIndex:k]);
}
}
}
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
enter code here
Your entities are very confusing because you did not pick usable entity names. You were too confused to lay out these simple relationships yourself. This results in chaotic code and does not allow you to think things through in a structured way.
Your code is completely incomprehensible. You have a data array despite a fetched results controller (presumably). The second part of your code sports a mysterious and cryptic new entity JOBsob. No way you can ask a meaningful question like this, let alone get an answer.
You have nested arrays without any type checking which is bound to break and in any way completely impossible to debug. Get rid of all of these.
Nevertheless, let's five it a try to get you started:
First, it does not make sense to use the plural for the entity name. If the entity represents a "form", it should be Form not Forms.
Maybe you want this setup:
Form <<----> Job <----->> JobDetail
One job has many forms and many job details. So the Form has a relationship job while the Job has a relationship forms. Similarly, a Jobdetail has a relationship job while Job has a relationship details.
When you have a form and create a new job you can only assign one job to it. Thus, the old job (if any) relationship would be broken.
oldForm.job = newJob;
This is a much safer way to assign relationships. Of course, you have created NSManagedObject subclasses for these entities for this purpose.
If however, the relationship between Job and Form is one-to-many in the other direction, your scheme would look like this.
Form <---->> Job <------>> JobDetail
I do not really now what a "form" would mean in this case - I will just rename it Project for clarity.
Project <---->> Job <------>> JobDetail
Now you can assign the a new job to a project and link the other relationships like this:
newJob.project = existingProject;
newJobDetail.job = newJob;

Core Data read only managed objects on thread not returning result to delegate

I need to use some core data managed objects in an NSOperation. The problem is that core data is not thread safe and apparently the object can't be loaded from the new thread. Does anybody know a good tutorial for this? I need the object read only... so the thread will not modify them in any way. Some other, unrelated entities may be added on the main thread while these objects are used in the background, but the background entities don't need to be modified at all..
Hmm seemed I fixed the background running issue, but now the problem is nothing is returned to the delegate... Why? In the thred if I nslog the results are all shown but that call to the delegate never happens
This is the code:
-(void)evaluateFormula:(Formula *)frm runNo:(NSUInteger)runCount{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:2];
NSManagedObjectID *formulaId = frm.objectID;
for (int i = 0; i < runCount; i++) {
NSInvocationOperation * op = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(runFormula:) object:formulaId];
[queue addOperation:op];
}
}
-(void)runFormula:(NSManagedObjectID *)fId {
NSManagedObjectContext *thredContext =[[NSManagedObjectContext alloc] init];
NSPersistentStoreCoordinator *coord = (NSPersistentStoreCoordinator *)[(PSAppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator];
[thredContext setPersistentStoreCoordinator:coord];
Formula *f = (Formula *)[thredContext objectWithID:fId];
NSDictionary *vars = [self evaluateVariables:[f.hasVariables allObjects]];
NSMutableString *formula = [NSMutableString stringWithString:f.formula];
for (NSString *var in [vars allKeys]) {
NSNumber *value =[vars objectForKey:var];
[formula replaceOccurrencesOfString:var withString:[value stringValue] options:NSCaseInsensitiveSearch range:NSMakeRange(0, [formula length])];
}
//parse formula
NSNumber *result = [formula numberByEvaluatingString];
// NSLog(#" formula %# result : %d",formula,[result intValue]);
//aggregate results
[self performSelectorOnMainThread:#selector(aggregate:) withObject:result waitUntilDone:YES]; // the delegate doesn't get called ...
}
-(void)aggregate:(NSNumber *)res {
[self.delegate didReceiveResult:res];
}

Setting a predicate for different tableviews

This one has been a big problem for me, and i´m still stuck with it so i was hopping that someone could give me some kind of guidance.
What i have is:
3 tableviews with multiple cells and each cell with several textfields.
1 tableview that appears inside a popover every time a specific textfield on those cells is pressed. This tableview has all!! the core data methods to retrieve the necessary data from my database.
Everything works ok...but i need to distinguish what kind of data shall appear in tableview 1 or 2 or 3...So i know i have to use predicate!.
What i have done: ( and i have tried other things)
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController == nil)
{
NSFetchRequest *fetchRequestList = [[NSFetchRequest alloc] init];
NSEntityDescription *entityList = [NSEntityDescription entityForName:#"List" inManagedObjectContext:self.managedObjectContext];
[fetchRequestLista setEntity:entityList];
TableViewOne *table1 = [[Cobertura alloc]init];
TableViewTwo *table2 = [[Cobertura alloc]init];
if (table1 textFieldShouldBeginEditing:table1.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview1];
}
if (table2 textFieldShouldBeginEditing:table2.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview2];
}
NSSortDescriptor *cellTitle = [[NSSortDescriptor alloc] initWithKey:#"reference" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:cellTitle, nil];
[fetchRequestLista setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequestLista managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"referencia" cacheName:nil];
_fetchedResultsController.delegate = self;
self.fetchedResultsController = _fetchedResultsController;
return _fetchedResultsController;
}
In each of my tableviews, i have an instance of the "popoverTableview" in my method textFieldShouldBeginEditing:
popoverTableview = [self.storyboard instantiateViewControllerWithIdentifier:#"popoverTableview"];
popover = [[UIPopoverController alloc] initWithContentViewController:popoverTableview];
[popover presentPopoverFromRect:textField.bounds inView:textField permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
popoverTableview.delegate = self;
popoverTableview.popView = self.popover;
So, if i´m in tableview1 i need to get [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview1];
Should i be creating some kind of method that my tableviewS could access? What am i forgetting here or not paying attention?
Thanks in advance, and any kind of advise would be welcome!
Regards
For everyone that was experiencing the same problem that i was, here is what i have done to resolve:
This is when i´m creating the popoverview when a specific textfield is pressed:
popoverTableview = [self.storyboard instantiateViewControllerWithIdentifier:#"popoverTableview"]initWithTextFieldTag:myTextFieldThatWasPressed.tag]
popover = [[UIPopoverController alloc] initWithContentViewController:popoverTableview];
[popover presentPopoverFromRect:textField.bounds inView:textField permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
popoverTableview.delegate = self;
popoverTableview.popView = self.popover;
popoverTableview.aIntVariable = myTextFieldThatWasPressed;
then in my popovertableview:
- (id)initWithTextFieldTag:(int)textFieldTag
{
self.aIntVariable = textFieldTag;
return self;
}
Then in the fetchedResultsController method, you´ll just have to create simple if´s telling wich predicate you want...
Regards
If the fetched results controller is for the popover table and you need to know in which table the text field was selected, I'd recommend tagging each of the text fields when you create them and creating an int _currentTable instance variable. That way, when your textFieldShouldBeginEditing: method is called, you can set the ivar's value with the tag and check that tag when creating the fetched results controller for the popover table.
So, instead of
if (table1 textFieldShouldBeginEditing:table1.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview1];
}
if (table2 textFieldShouldBeginEditing:table2.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview2];
}
you'll have
if (_currentTable == 1) {
fetchRequestList.predicate = // table one predicate
} else if (_currentTable == 2) {
fetchRequestList.predicate = // table two predicate
}
UPDATE:
This is how I would override the init from code. In your popover table view controller implementation:
- (id)initWithTableTag:(int)tableTag
{
self = [super init];
if (self) {
_currentTable = tableTag;
}
return self;
}
(Make sure you also declare - (id)initWithTableTag:(int)tableTag; in your header.) Then, when you create and present the popover controller (which I'm assuming you're doing in the textFieldShouldBeginEditing: delegate call):
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
// ...
YourPopoverTableViewControllerClass *vc = [[YourPopoverTableViewControllerClass alloc] initWithTableTag:textField.tag];
// ...
// display the popover
return YES;
}
Unfortunately, I don't know how to do this using storyboards.

Core Data - to many Reationships - having trouble with sets

I have look around for something similar with no luck so I am going to try and explain my trouble and paste some code. I have an application that uses Core Data and I can save and retrieve data from their respective textFields with the exception of my (to many relationships). I believe these are saved and returned as sets when fetched. I have read up on NSSet and looked at some code but with still do not understand how to code it.
Thanks for any help.
Hudson
- (IBAction) findContact
{
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Place"
inManagedObjectContext:context];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
[fetchRequest setEntity:entity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"placeName = %#", placeName.text];
[fetchRequest setPredicate:pred];
Place *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
if ([objects count] == 0) {
status.text = #"No matches";
}else{
matches = [objects objectAtIndex:0];
address.text = [matches valueForKey:#"address"];
city.text = [matches valueForKey:#"city"];
state.text = [matches valueForKey:#"state"];
zip.text = [matches valueForKey:#"zip"];
phone1.text = [matches valueForKey:#"phone1"];
email.text = [matches valueForKey:#"email"];
website.text = [matches valueForKey:#"website"];
phone2.text = [matches valueForKey:#"phone2"];
about.text = [matches valueForKey:#"about"];
photoName.text = [[matches photo]valueForKey:#"photoName"];
status.text = [NSString stringWithFormat:#"%d matches found", [objects count]];
NSSet *groupForSections = [groupForSections valueForKey:#"sections"];
for (Group *group in groupForSections) {
NSLog(#"group name = %#", [groupForSections valueForKey:#"groupName"]);
groupName.text = [group valueForKey:#"groupName"];
NSSet *sectionForPlaces = [sectionForPlaces valueForKey:#"places"];
for (Section *section in sectionForPlaces) {
sectionName.text = [section valueForKey:#"sectionName"];
NSLog(#"section name = %#", [section valueForKey:#"sectionName"]);
}
}
}
}
![enter image description here][1]
According to your follow-up comments, Place does not actually have a to-many relationship to Section, and Section does not actually have a to-many relationship to Group. Instead, Group has a to-many relationship (sections) with Section and section's inverse to-1 relationship is called group. Also, Section has a to-many relationship (places) with Place and Place's inverse to-1 relationship is called section.
If all those assumptions are correct (and without seeing your model it's hard to know if that is correct), your code should now be something like:
NSManagedObject *sectionForPlace = [matches valueForKey:#"section"];
sectionName.text = [sectionForPlace valueForKey:#"sectionName"];
NSManagedObject *groupForSection = [sectionForPlace valueForKey:#"group"];
gromName.text = [groupForSection valueForKey:#"groupName"];
This code doesn't directly access the fields through the NSManagedObject classes you've created, which should also work if you prefer that way.

Resources