NSFetchedResultsController not sorting on initial fetch - objective-c

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

Related

Mogenerator with NSFetchedResultsController - 0 sections

I am populating a UITableView from Core Data using an NSFetchedResultsController. The data is coming from an NSManagedObject subclass generated by Mogenerator called MenuItem. The MenuItem entity has a SectionID parameter that is an NSNumber and this is used to determine what section the item should be in within the table view.
I performed a test fetch on the data and confirmed that Core Data was populated correctly. All was fine.
The NSFetchedResultsController is created as follows and the sectionKeyNamePath is set to #"sectionId":
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MenuItem" inManagedObjectContext:self.persistenceController.moc];
[fetchRequest setEntity:entity];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"sectionId" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:#"rowId" ascending:YES]];
NSFetchedResultsController *frc = nil;
frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.persistenceController.moc
sectionNameKeyPath:#"sectionId"
cacheName:nil];
The problem (and solution):
With this code, the sections are not identified. The NSFetchedResultsController always returns 0. This worked previously when I created the NSMO subclass manually, so I figured it was something related to Mogenerator.
If I change the sectionNameKeyPath to be #"primitiveSectionId" then it works.
Hopefully this will help somebody in the future, but I don't understand what is going on here. Please could somebody explain why this fixes the problem?
Thanks
Why are you setting the FRC to nil? Try this (it works for me using an NSNumber Attribute):
With mogenerator you can get the entity using "[MenuItem entityName]" and access attributes using MenuItemAttributes.sectionId, for example.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[MenuItem entityName]];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:MenuItemAttributes.sectionId ascending:YES], [NSSortDescriptor sortDescriptorWithKey:MenuItemAttributes.rowID ascending:YES]];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.persistenceController.moc
sectionNameKeyPath:MenuItemAttributes.sectionId
cacheName:nil];
// Make sure you set your frc as the delegate
frc.delegate = self;
NSError *error = nil;
if (![fetcher performFetch:&error]) {
/*
* Did you execute the request?
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"%lu", [frc.sections count]);

Using an NSPredicate to access self.items

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"];

Fetch Relationship Objects

CoreData beginner
I have a simple problem with CoreData. My model has two entities, now called A and B. Entity A has a to many relationship of B entities, which has a inverse relationship to entity A.
I'm retrieving entities A with this code:
NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"A"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"name"
ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:descriptor]];
NSError *error = nil;
NSArray *items = [context executeFetchRequest:request error:&error];
if (error) /* ... */;
for (id item in items)
{
/* ... */
}
[request release];
[descriptor release];
Now I'd like to retrieve, inside that loop, an array of all the objects B pointed by the relationship of A. How can I achieve this? Should I create another fetch request or there is a more practical way?
I've searched StackOverflow and found similar questions, but too vague sometimes.
NSFetchRequest has an instance method on it called -setRelationshipKeyPathsForPrefetching:.
This method takes an array of key names that will be used to prefetch any objects defined in relationships with those key paths. Consider your example, updated with the new code:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
NSString *relationshipKeyPath = #"bObjects"; // Set this to the name of the relationship on "A" that points to the "B" objects;
NSArray *keyPaths = [NSArray arrayWithObject:relationshipKeyPath];
[request setRelationshipKeyPathsForPrefetching:keyPaths];
Now once you complete your fetch request, all of those relationship objects should be faulted in and ready to go.

displaying in a UITableView CoreData objects ordered in a Set

I have difficulties displaying objects coming from CoreData in a tableView.
I have 2 sorts of entities : Sample and SampleList. What is important to know is that a SampleList has an attribute sampleSet which is a set of samples (entity of Sample)
First I succeeded in displaying every SampleList. Here is viewDidLoad:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SampleList" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastSampleDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle error
}
[self setSampleListArray:mutableFetchResults];
[mutableFetchResults release];
[request release];
Once I click on a row in my tableView, I would like to display in another UITableView every sample from the SampleList selected.
I thought that I could pass to the subview SampleList mySampleList. But then, I don't know what to do with it as it is not organized.
How can I return an ordered array of Sample (ordered by dateSample attribute for example) ?
Thank you for your time !
You can just use NSSet and NSArray methods on sampleSet to get an ordered array:
sortedArray = [[sampleSet allObjects] sortedArrayUsingSelector:#selector(compare:)];
or if you want to specify particular sort descriptors instead of the regular "compare" method:
sortedArray = [[sampleSet allObjects] sortedArrayUsingDescriptors:sortDescriptors];

Removing an Object with A Certain Title from an NSTreeController

I am wondering how I could delete an object depending on it's title for the CoreData 'name' property I have.
To Add an Object I use this code:
NSManagedObjectContext *moc = [self managedObjectContext];
JGManagedObject *theParent =
[NSEntityDescription insertNewObjectForEntityForName:#"projects"
inManagedObjectContext:moc];
[theParent setValue:nil forKey:#"parent"];
// This is where you add the title from the string array
[theParent setValue:#"myTitle" forKey:#"name"];
[theParent setValue:[NSNumber numberWithInt:0] forKey:#"position"];
But I can't seem to find an equivalent function to remove An object.
I don't know if you looked in the Core Data Programming guide in the section for adding and deleting objects.
Edit
I've modified this to delete from an array of names. Again; less then 5 minutes work with the Predicate Programming Guide.
- (void)removeObjectsWithNames:(NSArray *)nameArray {
// Get the moc and prepare a fetch request for the required entity
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
// Create a predicate for an array of names.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name IN %#", nameArray];
[request setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
// Execute the fetch request put the results into array
NSError *error = nil;
NSArray *resultArray = [moc executeFetchRequest:request error:&error];
if (resultArray == nil)
{
// Diagnostic error handling
NSAlert *anAlert = [NSAlert alertWithError:error];
[anAlert runModal];
}
// Enumerate through the array deleting each object.
// WARNING, this will delete everything in the array, so you may want to put more checks in before doing this.
For (JGManagedObject *objectToDelete in resultArray ) {
// Delete the object.
[moc deleteObject:objectToDelete];
}
}
Edited 10/10/2009 - To add what Joshua has tried.
for(NSString *title in oldTasks) { // 1
// Get the moc and prepare a fetch request for the required entity
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"projects" inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
// Create a predicate for an array of names.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title IN %d", oldTasks]; // 2
[request setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
// Execute the fetch request put the results into array
NSError *error = nil;
NSArray *resultArray = [moc executeFetchRequest:request error:&error];
if (resultArray == nil)
{
// Diagnostic error handling
NSAlert *anAlert = [NSAlert alertWithError:error];
[anAlert runModal];
}
JGManagedObject *objectToDelete = [resultArray objectAtIndex:0];
// Delete the object.
[moc deleteObject:objectToDelete];
}
Notes
I've highlighted two lines.
You've pasted my example as a for loop rather than a function call. This is just taking the strings off one at a time and passing them into the method. In my example, I'm passing in an array of strings that you want to match.
This is where you are having your problem. If you'd bothered reading the the Predicate Programming Guide, right at the top, in the Predicates Basics section it says it expects the class that it is being used with should be KVC compliant. This is why you are getting the error about KVC compliance. You are trying to search for title IN... but title isn't a property of your model.
I think you may be confused about what a predicate does. Look at the exaple code that I wrote.
Firstly, I create a Fetch request which will select objects from the 'Projects' entity.
Secondly, I create a predicate which says for each object returned by the fetch request, get the value of the 'name' property and compare it to the values of the objects in the 'namesArray'
Thirdly, I'm creating a sort descriptor that will sort the results in ascending order based on the 'name' property.
Then, once I've set this fetch request up, I run it against the moc and it returns an array of objects that match these criteria.