For various reasons (summarized at the end) I am experimenting with using an NSFetchedResultsController to return some NSManagedObjects.
Specifically, a Person has many Cars, modeled using the core data relationship cars. I want to add another method to Person to return the same cars as self.cars, but using a FRC.
I think I am making a basic error with my NSPredicate, which is designed to only find the cars where car.person == self:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Car"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"dateAdded" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"person == '%#'", self];
[fetchRequest setPredicate:predicate];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
NSError *error;
BOOL success = [controller performFetch:&error];
No cars are being returned at all. Is my predicate incorrect?
Side Note - why not just use self.cars?
Like I say this is an experiment. I am hoping to benefit from the persistent cache of an NSFetchedResultsController.
The single quotes in the predicate are wrong, it should be
[NSPredicate predicateWithFormat:#"person == %#", self];
In the example you are fetching "Person" objects. Maybe you want to fetch "Car" objects instead?
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Car"];
Related
I am working on an app, where I want to display a UITableView filled with student projects. Each project entity has several attributes like title, description, etc. and is also linked with relationships to other entities like students, study course and so on.
What I am trying to do now, is, to use the study courses as sections in which are all the respective projects which are made by students from that study course.
So far, my code looks like this
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:Nil
cacheName:Nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
return _fetchedResultsController;
}
and this is a screenshot of the CoreData model.
I am really new to Obj-C and haven't designed the data model myself, so I'm kind of lost here and would be really glad about any help.
You haven't asked a specific question yet, but I can give you some basic direction. The way you're creating your NSFetchedResultsController looks correct except that you need to do two more things: sort the result by course title and set the sectionNameKeyPath to course title (I'm assuming your Studycourse entity has a title property, but substitute another property as needed).
- (NSFetchedResultsController *)fetchedResultsController
{
...
NSSortDescriptor *courseTitleSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"projecttostudycourse.title" ascending:YES];
NSSortDescriptor *titleSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES];
NSArray *sortDescriptors = #[courseTitleSortDescriptor, titleSortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"projecttostudycourse.title"
cacheName:Nil];
...
}
Note that by providing two sort descriptors, the projects will first be organized by course and then, within each corse, by project title. The other thing worth noting is that key paths use dot notation to follow entity relationships. So the way to reach the course title from a project entity is #"projecttostudycourse.title".
I have a strong pointer with the name "sensor" to an object of the class "SensorDB".
I tried to create a NSFetchedResultController so that my UIViewController gets notified, if something changes inside the "sensor" object.
To get the desired object into my NSFetchedResultController, I created following NSPredicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self == %#", self.sensor];
The goal is, that I have just one object in the NSFetchedResultController and get notifications about any updates, so that I can update my GUI.
But if I run my application, the app crashes when I try to alloc and init the NSFetchedResultController.
Here's my NSFetchedResultController code:
- (NSFetchedResultsController *)fetchedResultsControllerSensor
{
if (_fetchedResultsControllerSensor != nil) {
return _fetchedResultsControllerSensor;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SensorDB" inManagedObjectContext:dbHandler.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self == %#", self.sensor];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:dbHandler.managedObjectContext sectionNameKeyPath:nil cacheName:#"SensorCache"];
self.fetchedResultsControllerSensor = theFetchedResultsController;
_fetchedResultsControllerSensor.delegate = self;
return _fetchedResultsControllerSensor;
}
Thank you for your help or another/better approach
Linard
I thought, that I don't need a sort descriptor, because I've just one result object, but it looks like that the initialization of a NSFetchedResultController crashes, if there isn't at least one NSSortDescriptor.
That's now my NSPredicate and NSSortDescriptor:
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"(SELF = %#)", self.sensor];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"displayOrder" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
What is wrong with this code?
I know this is a silly predicate. But it's just to show that if this was working, it should be filter everything out, right?
Somehow, it's not. I'm getting every Month object in my DB, when I should be getting none.
I guess this tells me that the problem might not be in the predicate?
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Month" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate: [NSPredicate predicateWithFormat:#"1 == 0"]];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"month_" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:#"month_"
cacheName:#"Root"];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[_fetchedResultsController sections] count];
}
What I actually wanted to do is,
[NSPredicate predicateWithFormat:#"year.year_ == %d", year]
And I also tried this,
[NSPredicate predicateWithFormat:#"month_ == %d", 1]
Just to see if it works. But nothing.
edit:
At this point, no matter what predicate I set, as long it's a valid, well formatted predicate. It doesn't complaint, and get's me all objects. It's just not filtering anything.
I'm pretty much using boiler plate code from Apple's documentation.
Can anyone help me figure this out?
Thank you!
If using a cache, then the fetchRequest's cache should be deleted, before being reused.
Calling deleteCacheWithName: fixed the problem.
I am having a Core Data problem with NSFetchedResultsController. I have a one to many relationship between a parent and child entity. The array in the childFetchedResults.fetchedObjects property is NOT sorted by number (number is an int32 property in the model). It doesn't seem to matter if I use the MagicalRecord convenience category methods or not.
NSFetchRequest *req = [Child MR_requestAllSortedBy:#"number" ascending:YES withPredicate:[NSPredicate predicateWithFormat:#"parent = %#", self.parent]];
childFetchedResults = [[NSFetchedResultsController alloc] initWithFetchRequest:req managedObjectContext:[NSManagedObjectContext MR_defaultContext] sectionNameKeyPath:nil cacheName:nil];
childFetchedResults.delegate = self;
NSError *error;
[childFetchedResults performFetch:&error];
NSLog(#"fetched objects: %#", childFetchedResults.fetchedObjects);
However, if I sort the array of fetched objects using the exact same sort descriptor, it works fine:
NSLog(#"fetched objects: %#", [childFetchedResults.fetchedObjects sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"number" ascending:YES]]]);
I gather you can only use comparators which Core Data can pass on to the SQLite store when specifying sort descriptors for a fetch request. But I feel like that shouldn't matter in this case since sorting by a number has got to be supported by SQLite.
Anyone solved this? I feel like it's a similar issue to the one described here: NSSortDescriptor not being called.
As for MR_requestAllSortedBy, it's in MagicalRecord, here is the implementation:
+ (NSFetchRequest *) MR_requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self MR_requestAllInContext:context];
NSSortDescriptor *sortBy = [[NSSortDescriptor alloc] initWithKey:sortTerm ascending:ascending];
[request setSortDescriptors:[NSArray arrayWithObject:sortBy]];
return request;
}
So this was caused by fetching against a nested MOC with unsaved changes. Either fetching with the parent MOC or saving the nested MOC prior to executing the fetch resolves the problem. Similar to what was going on in this question: NSSortdescriptor ineffective on fetch result from NSManagedContext
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"RemainderDataBase" inManagedObjectContext:[self managedObjectContext]];
NSSortDescriptor *nameSort = [[NSSortDescriptor alloc]initWithKey:#"title" ascending:YES];
NSArray *sortDescriptor = [[NSArray alloc]initWithObjects:nameSort, nil];
fetchRequest.sortDescriptors = sortDescriptor;
NSPredicate *p1=[NSPredicate predicateWithFormat:#"startdate > %#", [NSDate date]];
[fetchRequest setPredicate:p1];
[fetchRequest setEntity:entity];
i think you are looking for the above code..
Here is the case. I have 2 models:
Sport (with attribute name and relationShip players)
Player (with attribute name and relationShip sport)
In a ViewController, I wan't to display all players in a UITableView ordered by sport.
When a player name changes, NSFetchedResultsControllerDelegate callbacks are called.
But when the name of a Sport is updated, no NSFetchedResultsControllerDelegate callbacks are called.
Is this a normal behaviour ? How can I know the updates of Sport.name (without creating another NSFetchedResultsController) ?
Thank you and Best Regards.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"sport.name != NULL"];
[fetchRequest setPredicate:predicate];
NSArray *sortDescriptors = [NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:#"sport.name" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES],
nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Player"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entityDescription];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"sport.name"
cacheName:nil];
[fetchRequest release];
fetchedResultsController.delegate = self;
NSError *error = nil;
if ([fetchedResultsController performFetch:&error] == NO) {
ALog(#"Fetch error: %#", [error localizedDescription]);
}
You can using a tip:
If (fetchresultcontroller != nil || isOtherEnthityUpdate)
{
return fetchresultcontroller;
}
And do self.tableview reload from any place where u check other entity. It can be notification center or other tableview.