NSFetchedResultsController with NSSortDescriptor one to one - ios

Want to sort by name which have one to one relationship using NSSortDescriptor in NSFetchedResultsController. Here my code
NSFetchRequest <studentDetail *> *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[studentDetail entityName]];
NSSortDescriptor *sortDescriptorForSchoolName = [NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector(#selector(schoolName))
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *sortDescriptorForSchool = [NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector(#selector(school))
ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptorForSchool, sortDescriptorForSchoolName]];
[fetchRequest setFetchBatchSize:4];
NSManagedObjectContext *mainContext = [[coreDataManager sharedManager] managedObjectContext];
NSFetchedResultsController *FRC = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:mainContext
sectionNameKeyPath:nil
cacheName:nil];
[FRC setDelegate: self];
NSError *fetchError = nil;
if (![FRC performFetch: &fetchError])
NSLog(#"fetch error: %#", fetchError);
[self setStudentDetailFetchedResultsController: FRC];
Example DB format:
School and student are entity name.
School relationship with student one to many.
Total 4 school. Each school have 4 student.
Now I want to show all student list with name ascending sort.
In UITableView I am showing student details. There is a option to sort students are sort by school entity. In my FRC have studentDetail. How can I sort those details with FRC?.
From studentDetail entity have one to one relationship with school.
Update
school entity one to many
school1
relationship
->student1
->student2
->student3
->student4
school2
relationship
->student5
->student6
->student7
->student8
school3
relationship
->student9
->student10
->student11
->student12
school4
relationship
->student13
->student14
->student15
->student16
student entity one to one
student1
relationship
->school1
student2
relationship
->school1
student3
relationship
->school1
student4
relationship
->school1
student5
relationship
->school2
student6
relationship
->school2
student7
relationship
->school2
student8
relationship
->school2
student9
relationship
->school3
student10
relationship
->school3
student11
relationship
->school3
student12
relationship
->school3
student13
relationship
->school4
student14
relationship
->school4
student15
relationship
->school4
student16
relationship
->school4
Got answer from thanks to #RunLoop. But this need few change which I want.
As I mention in my code for to sort NSStringFromSelector(#selector(school)) like, example: [NSSortDescriptor sortDescriptorWithKey[NSString stringWithFormat:#"%K.%K", NSStringFromSelector(#selector(school)), NSStringFromSelector(#selector(schoolName))] ascending:YES] Instead of [NSSortDescriptor sortDescriptorWithKey:#"school.schoolName" ascending:YES]
If I use this kind of method, In feature if I change any name in entity it's show warning and static one school.schoolName doesn't. Is that possible?
I tried this method with NSPredicate that doesn't show any warning. How should I solve this?

The following code will fetch the students, ordered by schoolName then by studentName:
NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:#"Student"];
NSSortDescriptor *schoolNameSD = [NSSortDescriptor sortDescriptorWithKey:#"school.schoolName" ascending:YES];
NSSortDescriptor *studentNameSD = [NSSortDescriptor sortDescriptorWithKey:#"studentName" ascending:YES];
fr.sortDescriptors = #[schoolNameSD, studentNameSD];
NSManagedObjectContext *moc = [[coreDataManager sharedManager] managedObjectContext];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fr
managedObjectContext:moc
sectionNameKeyPath:nil
cacheName:nil];
[frc performFetch:nil];
If you only want to sort by studentName, simply remove the schoolNameSD and ask frc to perform the fetch again.

Related

NSFetchedResultsController - grouping results from a single entity

Let's say that my Core Data model contains the following entity, Family:
I'd like to use NSFetchedResultsController to display the contents of the Family in a UITableViewController in the following format where parents are "sections" with children listed under a "parent":
In my view controller file, I have the following:
- (void)setFRC
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:[Family entityName]];
NSSortDescriptor *sort1 = [[NSSortDescriptor alloc] initWithKey:#"Child" ascending:YES];
NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:YES];
NSArray *sorters = [NSArray arrayWithObjects:sort1, sort2, nil];
[request setSortDescriptors:sorters];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[MYCoreDataManager managedObjectContext] sectionNameKeyPath:#"Child" cacheName:nil];
}
When using the code above, however, I'm getting the following table instead:
Can somebody tell me how to get the "children" grouped underneath the proper "parent"? I realize that the data model could be separated such that there are separate entities for child and parent; however, I'm working with legacy code and don't have the luxury of modifying the data model for the time being.
I think you need to use predicates:
Get all Parents
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Child == %#", #NO];
Loop through the parents and get all their Children
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Child == %# AND Parent == %#", #YES, parent.Name];
This solution results in an array construct. Not sure, this answer is helping you, since you're looking for a NSFetchedResultsController.

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
}

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.

NSFetchedResultsController delegate callback for relationship

Suppose you have two entities, one is People, the other is Location. Location has an attribute name, People has a to-one relationship to Location named location.
Then if you have an NSFetchedResultsController like below, you can't get the delegate callback when you change the Locations name.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"People"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"location.name"
ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"location.name"
cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
I know I can fetch the Location entity and then filter it by name to solve this problem. But I'm curious about this, does the NSFetchedResultsController delegate just notify you when something changed in the entity's relationship,not the relationship's attribute.
Can anyone give me some posts about this feature. I search the web and Apple's documentation but can't find a reasonable description.
does the NSFetchedResultsController delegate just notify you when something changed in the entity's relationship,not the relationship's attribute.
Exactly. Because a change in the relationship is a change in the object that the FRC is observing. But a change in the properties of one of the objects at the other end of the relationship will not be notified to the delegate because those objects are not being observed by the FRC.

Fetching specific data from a selected entry

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").

Resources