Core data doesn't fetch objects - iphone

Hello here is my NSFetchedResultsController implementation:
-(NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSLog(#"context - %#",self.context);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Categories" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:#[sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.context sectionNameKeyPath:nil
cacheName:#"root"];
_fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
// [[self context] fetchObjectsForEntityName:#"Categories" withPredicate:
// nil] ;
return _fetchedResultsController;
}
fetchedResultsController.fetchedObjects is always nil.
Here is how I'm adding objects:
-(void)addCategoryWithName:(NSString *)name
{
NSLog(#"context - %#",self.context);
Categories *category = (Categories *)[NSEntityDescription insertNewObjectForEntityForName:#"Categories" inManagedObjectContext:self.context];
category.name = name;
NSLog(#"category name - %#",category.name);
category.displayOrder = [NSNumber numberWithFloat:1.0f];
// site.displayOrder = displayOrder;
NSError __block *error;
if (![self.context save:&error]){
NSLog(#"Error saving - %#", [error localizedDescription]);
}
}
There is no error, no nothing. If I see my .sqlite file in the Documents directory it's modified, but I can't get any objects fetched. Really weird, I'm using fundamentally the same codes for my other app and it works just fine.
Any ideas?

You have to call performFetch on the fetched results controller once. Otherwise it will not fetch anything and also not track changes. For example add
NSError *error;
if (![_fetchedResultsController performFetch:&error]) {
// error handling
}
at the end of the fetchedResultsController getter method.
Remark: It seems that specifying a cacheName without sectionNameKeyPath can cause problems, see UITableView with NSFetchedResultsController Does Not Load the Second Time. Since the cache is used to cache section information, it is not needed anyway if you don't have sections.
Update: The preceding remark is no longer valid, it turned out that the problems in the linked question had a different cause.

Related

Core Data - Distinct Results not working in fetchedResultsController

I have spend hours trying to figure the following out...
I have the following Core Data fetchResultsController in my iPhone App which does not return me a distinct set of values despite setting the following in my code...
// Only distinct values
[fetchRequest setReturnsDistinctResults:YES];
The following is the whole fetchResultController ...
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"CardMessage"inManagedObjectContext:self.managedObjectContext]];
// Set the properties to fetch
NSArray *propertiesToFetch = [[NSArray alloc] initWithObjects:#"category", nil];
[fetchRequest setPropertiesToFetch:propertiesToFetch];
// Only distinct values
[fetchRequest setReturnsDistinctResults:YES];
// Order the output
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"category" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
Your propertiesToFetch array contains a string. It should contain an NSPropertyDescription. (In this case it will be a subclass thereof, NSAttributeDescription.)
NSArray *propertiesToFetch = [[NSArray alloc] initWithObjects:
[entity.propertiesByName objectForKey:#"category"],
nil];
(modern Objective-C)
NSArray *propertiesToFetch = #[entity.propertiesByName[#"category"]];
If you still get double entries, just use NSSet to get unique values.
NSSet *uniqueProperties = [NSSet setWithArray:resultsArray];
Note both the remarks from the setPropertiesToFetch documentation:
You must set the entity for the fetch request before setting this value, otherwise NSFetchRequest throws an NSInvalidArgumentException exception.
This value is only used if resultType is set to NSDictionaryResultType.
Note that the default for resultType is NSManagedObjectResultType.

CoreDataBooks Example - Reorder Cells?

I am using the CoreDataBooks example code in my project and have implemented it nicely. I am attempting to add the ability to reorder the cells in the UITableView, however I am really struggling.
I have looked at a few techniques, the following seems the most logical and straightforward:
UITableView Core Data reordering
However, I can't get it working, the most suspicious thing being it includes a sort descriptor which might be conflicting with the sort descriptor implemented through the CoreDataBooks example, possible?
I can't pinpoint the problem.
Edit
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create and configure a fetch request with the Book entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"GuestInfo" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"displayOrder" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] descriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"firstName" cacheName:#"Root"];
self.fetchedResultsController = aFetchedResultsController;
fetchedResultsController.delegate = self;
return fetchedResultsController;
}
- (void)viewWillAppear {
myTableViewData = [self getRowObjects];
[self.tableView reloadData];
}
In my viewDidLoad this happens:
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
Does that call fetchedResultsController or throw an error if it doesn't exist? I can't see what else would cause the problem.

perform new fetch in core data iphone

I am doing core data fetches using the standard code provided by Apple, it has the following statement in the beginning of the fetch method.
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController_ != nil) {
return fetchedResultsController_;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:self.entityName inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:self.sortFieldName ascending:YES];
NSMutableArray *sortDescriptors = [[NSMutableArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
if(predicate != nil)
[fetchRequest setPredicate:predicate];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:sectionKeyName cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
aFetchedResultsController = nil;
[fetchRequest release];
fetchRequest = nil;
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
}
return fetchedResultsController_;
}
The first time, fetchedResultsController gets the required info. But I am setting a predicate on this result after that. So, I would like the fetch to consider my predicate the next time. If it goes in the above code, it will come out immediately because the old value is not nil.
To overcome this, after setting the predicate, I do
fetchedResultsController_ = nil;
This works ok,but is giving leaks at the fetchedResultsController as soon as the fetchRequest is allocated.
Is there a better way to re-execute the fetch or to avoid the leak?
Just add a release.
if(fetchedResultsController_){
[fetchedResultsController_ release];
}
fetchedResultsController_ = nil;

Core Data , NSFetchResultsController leaking

I am not sure if the leak is in my implementation or is it from apple's side....
Instruments indicate me that I have a leak in this line :
if (![[self fetchedResultsController]
performFetch:&error])
I am adding annotations by reading the fetchController to the Map.... like this :
-(void)fillMapWithAnnotations{
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
for(int a=0; a<[[[fetchedResultsController sections]objectAtIndex:0] numberOfObjects]; a++){
LookAround *look=(LookAround *)[fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:a inSection:0]];
if(look){
AddAnnotation *newAnnotation=[[AddAnnotation alloc]initWithLookAround:look];
if(newAnnotation){
[self.mapView addAnnotation:newAnnotation];
[newAnnotation release];
newAnnotation=nil;
}
}
}
}
and I initialize my FetchController like this:
- (NSFetchedResultsController *)fetchedResultsController{
// Set up the fetched results controller if needed.
if (fetchedResultsController == nil) {
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"LookAround" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
}
return fetchedResultsController;
}
I get a leak as soon as i Navigate Back, the ViewController gets Deallocated in which I release my fetch controller object.
The objects that leak are numerous (and of the same type I guess) around the number of records in my sqlite DB
Thanks in advance for your help....
As I noted above, the leak is probably in your AddAnnotation class.

How to properly configure NSFetchedResultsController

I am placing an NSFetchedResultsController into my code so I get that nice automatic sectioning of my table view data.
So I am running a test to make sure everything works properly. I have a single Book entity in my persistent store. I will first perform the fetch the old way, then I will try to use NSFetchedResultsController. The difference between the 2 blocks of code is just 2 lines.
Code without NSFetchedResultsController:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:kBookEntityName inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
//The following 2 lines will be replaced by the NSFetchedResultsController
NSMutableArray *mutableFetchResults = [[[self.managedObjectContext executeFetchRequest:request error:nil] mutableCopy] autorelease];
Book *result = (Book*)[mutableFetchResults objectAtIndex:0];
NSString* title = [result valueForKey:#"title"];
NSString* priority = [result valueForKeyPath:#"priority.name"];
[request release];
Now I substitute in the lines for the NSFetchedResultsController:
NSFetchedResultsController* fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"title" cacheName:#"BookList"];
Book *result = (Book*)[fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
Seems pretty cut and dry. The first code block properly fetches the single Book entity. The code with the NSFetchedResultsController, however, does not. Instead it returns nil.
My question is: Am I properly configuring the NSFetchedResultsController in this example?
(note, the fetchedObjects property of the NSFetchedResultsController is also nil)
I think you still need to tell the NSFetchedResultsController to actually perform the fetch:
NSError *error;
BOOL success = [controller performFetch:&error];
(taken from the example in the NSFetchedResultsController reference)
one other thing that seems odd: do you really want to use "title" as the sectionNameKeyPath? won't that basically create a separate section for each book?
You need to initialize your NSFetchedResultsController only once, as follows.The code assumes
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
are declared in the header of your controller, and that managedObjectContext is already properly initialized.
- (void)viewDidLoad {
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
NSLog(#"%d objects fetched", [[fetchedResultsController fetchedObjects] count]);
}
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:kBookEntityName inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
/* Optional settings
[request setResultType:NSManagedObjectResultType];
[request setIncludesPropertyValues:YES];
[request setIncludesPendingChanges:NO];
[request setReturnsObjectsAsFaults:NO];
*/
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];;
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:#"title" cacheName:#"BookList"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[request release];
return fetchedResultsController;
}