NSFetchedResultsController - grouping results from a single entity - ios

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.

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
}

Populating a tableview with child objects of parent entity

I have two tableviewcontrollers in a navigation controller hierarchy. The first contains parent entities, which I have successfully populated from a core data entity fetch request. I created a one-to-many relationship in the parent entity to the child entity and seem to be adding child entities okay. I can populate it with all the children of all the parents, but can't restrict it to the current parent. I have tried using predicates in the child tableview controller fetch request but without success.
Should I be using includesSubEntities and keeping the parent entity used in the request? If so, would I use the delegate protocol in some way?
The code below is for the child tableview. The commented-out bits are different attempts!
Thanks.
- (NSFetchedResultsController *)fetchSerials
{
if ( !_fetchSerials )
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity =
[NSEntityDescription entityForName:#"Serial"
inManagedObjectContext:AppDelegatePtr.managedObjectContext];
// NSPredicate *predicate = [NSPredicate predicateWithFormat:#"inExercise.name = '%#'", [[SMNetworkModel sharedInstance] selectedNarrative]];
// [fetchRequest setPredicate:predicate];
// [fetchRequest setRelationshipKeyPathsForPrefetching:#[#"inExercise"]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"narrative" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
_fetchSerials = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:AppDelegatePtr.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchSerials.delegate = self;
NSError *error;
if ( ![_fetchSerials performFetch:&error] )
NSLog(#"fetch error: %#", error.localizedDescription);
}
return _fetchSerials;
}
A stupid typo combined with a shortfall in my understanding of my new friend Core Data was all that was stopping Predicates from working.
I had mistakenly thought you passed objectIds around instead of the objects themselves.
Uncomment the two lines involving the predicate and change the selectedNarrative to the correct property and it all worked!

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.

NSFetchRequest / NSFetchedResultsController returning single property distinct but sorted using another attribute of the entity

I am using an NSFetchedResultsController to display data in a table. The data store contains lots of rows that have a groupID, and multiple entities can share a groupID.
Each entity has an insertion date property as well.
I would like to get a list of distinct groupID ordered by the insertion date property. The following code works, except it is not sorted by the insertion date property. I assume that since that property is not one of the ones being fetched, it is not available for sorting.
And I am using MagicalRecord as well, fwiw.
Is there a way to use other properties of the entity for sorting but not have them as part of the result? Adding in the insertion date to the fetched properties makes distinct superfluous since the dates are all unique as they are.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userID like[c] %#", THEUSERUSERID];
NSFetchRequest *fetchRequest = [AnEntity MR_requestAllWithPredicate:predicate];
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:#[#"groupID"]];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"inDate" ascending:NO];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
_frController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[NSManagedObjectContext MR_rootSavingContext] sectionNameKeyPath:nil cacheName:nil];
Chadbag ... I had the same problem. I researched the setPropertiesToFetch and found out it requires an NSArray of NSPropertyDescriptions ... not just the attribute name.
I think if you REMOVE this:
[fetchRequest setPropertiesToFetch:#[#"groupID"]];
and ADD this:
NSDictionary *entityProperties = [entity propertiesByName];
NSPropertyDescription *propDescription = [entityProperties objectForKey:#"groupID"];
NSArray *propArray = [NSArray arrayWithObject:propDescription];
[fetchRequest setPropertiesToFetch:propArray];
That should work for you ... it did for me!

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