Fetching specific data from a selected entry - ios

I am new to iOS Developing, and I'm getting my feet wet in Core Data. I've got an app that I'm slowly piecing together (thanks to help from this site!), but I'm stuck on how to fetch data.
The app:
My app has two main screens, both UITableViews: A "Class List" view where they can add new classes, and an "Add My data model has two entities: Course (for class) and Student, with a to-many relationship from Course-Student. Right now I have it working so that when a I tap on a class in the "Class List" view I am taken to the "Add Students" view where I can add students to the class, but my fetch results controller is returning all students that I have added. My question: how do I format the fetch request in the "Add Students" view to fetch only those students that should belong to that class? Here is the fetched results controller I have right now:
-(NSFetchedResultsController *) fetchedResultsController {
if (_fetchedResultsController !=nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Student" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"name" cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
I'm guessing it has something to do with predicates, but I haven't gotten that far in my learning. Any help at all would be appreciated. Thanks!

You are guessing right. To fetch all students that belong to a particular course,
add the following predicate to the fetch request:
Course *theCourse = ...; // your Course object
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"course = %#", theCourse];
[fetchRequest setPredicate:predicate];
(assuming that the to-one relationship from Student to Course is called "course").

Related

Core data don't return full objects, data: <fault>

I'm trying to fetch two entities eg: Student with email attribute and Professor, both of have same parent entity eg: Person with attributes entityId, firstName and lastName i want to generate them in two sections using NSFetchedResultsController.
Here is a part from getter for fetchedResultsController
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setFetchBatchSize:20];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *description = [NSEntityDescription entityForName:#"Person"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:description];
NSSortDescriptor *firstNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"firstName" ascending:YES];
NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:YES];
[fetchRequest setSortDescriptors:#[firstNameDescriptor, lastNameDescriptor]];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"entityId"
cacheName:nil];
All Students have the same entityId and all Professors too
In tableView I have two prototype cells one for Student and another for Professor.
I get two sections as expected but students are in different sections, i have printed all objects from fetchedResultsController in console like this, po [self.fetchedResultsController objectAtIndexPath: [NSIndexPath indexPathForItem:1 inSection:1]] all professors are printed with fault:
<Professor: 0x6080000befc0> (entity: Professor; id: 0xd0000000001c0002 <x-coredata://03A3ECAD-CCA7-424E-86F9-258D25372BA1/Professor/p7> ; data: <fault>)
I have forced the fetch request to return full objects using [request setReturnsObjectsAsFaults:NO] but it had no effect.
Why is it happening so?
To avoid "data fault" issue you should set this field of NSFetchRequest:
request.returnsObjectsAsFaults = NO;
To separate students from professors in two sections you can use multiple NSFetchedResultsControllers, as described here:
https://stackoverflow.com/a/2309855/1689376
To avoid duplication of your code, just create a method like this and call it twice:
- (NSFetchedResultsController) createFetchedResultsController: (NSString *) entityName {
//move your code here
}

to-many relation as sectionNameKeyPath

I want to show Project such that it shows all the projects on the basis of sharedToUsers basis. Means section name with User.user_id and it should shows all the shared project with that user. but i am not able to set section value for NSFetchedResultsController properly because it is a too many relation and it is crashing on sectionNameKeyPath .
Any Help
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *theParent = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:_managedObjectContext];
//the name key exsit in parent entity of project.
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:10];
[fetchRequest setEntity:theParent];
[fetchRequest setPredicate:_predicate];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_managedObjectContext sectionNameKeyPath:#"ANY sharedToUsers.user_id"
cacheName:[self getCacheNameForNSFetchedResultsController]];
if you have a senario like i have then when ever you change data in the core data then try donot use to add or update relation ship items using object of parent or category object. if you had to link some thing with category then try to link from child object.
donot use this
[category addRelationShipItem:child];
use this
`[child addToCategory:category];`
when you use this then it will not triger update call in NSFetchResultController call.
Happy coding.
Hi If you want fetch the Project shared by a user you need this predicate:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *theParent = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:_managedObjectContext];
NSString *userId; // If you user_id is a NSNumber change you to it.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY sharedToUsers.user_id = %#",userId];
[fetchRequest setPredicate:predicate];
Last things you are using a NSSort: name , but name it's not a Project property.

Filter NSFetchedResultsController to get only objects with some relationship

I have two entities, A and B, and the following relationships:
A -> B - To many
B -> A - To one
In other words: A can have zero or more B and B can have only one A.
I want to use NSFetchedResultsController to show my A entries in a table view, but i want to filter the results by A -> B relationship.
To do so, i have a UISegmentedControl, if the user taps the first segment i want to show only the A entries that have at least one relationship with B, and if the second segment is tapped i want to show only the entries with no relationships with B.
I'm using CoreData's NSManagedObject, so my A object has a NSSet property with all B entries in a relationships with A.
This is how i'm instantiating my NSFetchedResultsController:
NSManagedObjectContext *context = self.managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:"A" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:descriptorKey ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setEntity:entity];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:controllerKey
cacheName:nil];
NSError *error;
BOOL success = [controller performFetch:&error];
if (success) {
return controller;
}
This code get all A entries, how can i make that filter?
You need to add a predicate to your fetch request:
e.g.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"B.#count == 0"];
[fetchRequest setPredicate:predicate];
This will filter As that don't have any related B objects.
As #Abizern mentioned in comments, you need to add a NSPredicate to your NSFetchedResultsController. The predicate would be something like:
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"b == %#", myVarReferenceToB]];
If you only have a unique identifier in B (lets call it identifier) instead of an object reference you could write it as:
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"b.identifier == %#", myVarReferenceToBIdentifier]];
This will produce your filter.
Every time the user changes the segmented control you will need to re-build the fetch or you will need to keep one NSFetchedResultsController around per segment.

Fetching contacts based on tag

I'm trying to modify a simple Core Data fetch request for contacts to only look for contacts with a relationship with a certain tag. Contact and Tag are both entities with a many-to-many relationship.
I understand with Core Data I can do this by first fetching the Tag object, and then calling tag.contact, but I don't want to do it this way as the rest of the code is dependent on the fact that the fetchResultsController returns Contact objects, not Tag objects.
If I were to do relational databasing, I could do a simple cross-table query and find all contacts with a certain tag. Is there a simple way I can replicate this via Core Data?
-(NSFetchedResultsController *) fetchedResultsController {
//if fetch controller already exists
if(_fetchedResultsController != nil) {
return _fetchedResultsController;
}
//create a new fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact"
inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//instantiate the fetch controller with the fetch request and sort by last name into sections
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self managedObjectContext] sectionNameKeyPath:nil cacheName:nil];
//declare delegate of fetch controller as self
_fetchedResultsController.delegate = self;
NSLog(#"fetchResultsController Created");
return _fetchedResultsController;
}
Use NSPredicate.
Lets say you have related Contacts with Tag by name tags and tag entity has property name.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY tags.name = [cd] %#", #"sales"];
[fetchRequest setPredicate:predicate];

how to sort a view-based tableview

I can't manage to sort a view-based tableview. I use an arrayController that control an entity of core data.
I tried to select a column and in the attribute inspector I used as a sort key the attributes name relative to that column and compare: as a selector... when I build and run I click on the header and now display the arrow that change every click, but the nothing happens with the rows. no sorting.
How can I fix it?
I think I'm missing something easy, but I can't get over it.
You may want to consider using an NSFetchedResultsController. From the Apple documentation for NSFetchedResultsController,
You use a fetched results controller to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.
The code looks like this where the line that provides the sort descriptor starts with NSSortDescriptor.
- (NSFetchedResultsController *)fetchedResultsController
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YourEntityName"
inManagedObjectContext:yourManagedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"YourSortKey"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSArray *fetchedObjects = [yourManagedObjectContext executeFetchRequest:fetchRequest
error:&error];
if (fetchedObjects == nil) {
// Handle the error
}
yourFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
return yourFetchedResultsController;
}

Resources