NSFetchedResultsController loses his rows after entering background - ios

I've a strange problem in my app.
I instanciate a NSFetchedResultsController to populate my array.
It's declared this way (as a #propoerty (nonatomic, retain) )
- (NSFetchedResultsController *)fetchedContactsController {
if (self->fetchedContactsController != nil) return self->fetchedContactsController;
// Create the fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// I want every contact...
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact" inManagedObjectContext:self->managedObjectContext];
[fetchRequest setEntity:entity];
// ... whose are not associated with my order item
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT (%# IN items)", self.orderItem];
[fetchRequest setPredicate:predicate];
// Edit the section name key path and cache name if appropriate
self.fetchedContactsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self->managedObjectContext sectionNameKeyPath:nil cacheName:nil];
[aFetchedResultsController release];
[firstnameSortDescriptor release];
[lastnameSortDescriptor release];
[sortDescriptors release];
[fetchRequest release];
// Execute the fetch request
[self->fetchedContactsController performFetch:nil];
return self->fetchedContactsController;
}
of course, contacts.items exists.
When my app goes background then foreground again, if I do another
NSLog(#"before >>> %d", [[fetchedContactsController fetchedObjects]count]); // >>> 3
[self->fetchedContactsController performFetch:nil];
[tableView reloadData];
NSLog(#"after >>> %d", [[fetchedContactsController fetchedObjects]count]); // >>> 0
every line in my array disappear !
EDIT :
If I comment these lines :
// ... whose are not associated with my order item
//NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT (%# IN items)", self.orderItem];
//[fetchRequest setPredicate:predicate];
The problem don't occur anymore ! But that's not what I want...

Ok I found it. Thanks to this question, I replaced this line :
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT (%# IN items)", self.orderItem];
by
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT SELF IN %#", self.orderItem.recipients];
It's funny how the fact you ask a question can make you on the path to resolve.

Related

CoreData :NSFetchedResultsController one to many to many to many relationship

I working on app were I using core data and the core data object is is show below:
Giving theaterName and genreName I want to show the actorsName. Anyone know of a way to build a NSPredicate to do that?
You can use SUBQUERY to build NSPredicates that go deep into "to many" relationships:
// Set predicate
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:#"SUBQUERY(genres, $g, ANY $g.movies.name == %#).#count != 0", #"Batman vs Superman"];
[fetchRequest setPredicate:requestPredicate];
UPDATE
Based on your updated example, you'll need nested subqueries like this:
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:#"SUBQUERY(movies, $m, ANY $m.genres.name == %#).#count != 0 AND SUBQUERY(movies, $m, SUBQUERY($m.genres, $g, ANY $g.theaters.name == %#).#count != 0).#count != 0", #"Action", #"Harkins"];
Here is a sample Cast entity fetch:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Cast" 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:#"name" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
// Set predicate
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:#"SUBQUERY(movies, $m, ANY $m.genres.name == %#).#count != 0 AND SUBQUERY(movies, $m, SUBQUERY($m.genres, $g, ANY $g.theaters.name == %#).#count != 0).#count != 0", #"Action", #"Harkins"];
[fetchRequest setPredicate:requestPredicate];
// Fetch
NSError *error = nil;
if (![aFetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
// Check fetched objects
NSArray *fetchedObjects = [aFetchedResultsController fetchedObjects];
for (Cast *eachCast in fetchedObjects) {
NSLog(#"Each Cast name: %#", eachCast.name);
}
Just replace #"Action" with the desired Genre name and #"Harkins" with the Theater name.
Here is how I setup the core data model:

iOS Core data predicate for one-to-many

I have two entities as illustrate on the figure. I need to fetch a particular appointment and all the related attendees where attendee is not deleted (hasDeleted != 1). I have tried with different predicate formats. Following are two main predicate I have tried and not able to get the any of these predicates work. Could you please help me to overcome this issue?
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"objectName = %# AND objectId = %i AND SUBQUERY(attendees, $x, $x.hasDeleted != 1).#count != 0", [self.userDefaults stringForKey:OBJECT_NAME], [[self.userDefaults objectForKey:OBJECT_ID] intValue]];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"objectName = %# AND objectId = %i AND (ANY attendees.hasDeleted != 1)", [self.userDefaults stringForKey:OBJECT_NAME], [[self.userDefaults objectForKey:OBJECT_ID] intValue]];
Appointment list tableview
Detail appointment view where the attendee details can be seen.
Fetch result controller code:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Attendee" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"uuid" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"appointment.objectName = %# AND appointment.objectId = %i AND hasDeleted != 1", [self.userDefaults stringForKey:OBJECT_NAME], [[self.userDefaults objectForKey:OBJECT_ID] intValue]];
[fetchRequest setPredicate:predicate];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
A fetch request always returns the objects from the managed object context. You cannot
fetch "modified objects", such as Appointment objects with a modified relationship to
"Attendee" which contains only the attendees with a certain property.
What you can do instead is to fetch the undeleted Attendee object instead
which are related to the given Appointment.
Create a fetch request for the Attendee entity and use the predicate
[NSPredicate predicateWithFormat:#"appointment.objectName = %# AND appointment.objectId = %i AND hasDeleted != 1", [self.userDefaults stringForKey:OBJECT_NAME], [[self.userDefaults objectForKey:OBJECT_ID] intValue]]
Update according to the new information: In your first view controller you use a
fetched results controller with a fetch request for the Appointment entity.
If you want to display only appointments that have any attendee which has not cancelled
then add the predicate
[NSPredicate predicateWithFormat:"ANY attendees.hasDeleted != 1"]
to this fetch request.
When the user taps on an appointment, you pass the select appointment to the next view
controllers. To display the attendees for this appointment, use a fetched results
controller with a fetch request for the Attendee entity, and add the predicate
[NSPredicate predicateWithFormat:#"appointment = %# AND hasDeleted != 1", selectedAppointment]

Predicate Problems

I have a managed object with the field of journalHeaderID. I can see via NSLog that those items are properly being placed into Core Data. To display the data I need to filter by what is relevant to the view.
So as I'm configuring my view of this data I have created an array of IDs that are relevant. When I run the code, though, I get no results even though I know they are there.
I thought this predicate was the right one to use, but it's not working right and I am not sure why.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"journalHeaderID IN %#", idsToList];
Here's the array as it passees into the predicate, and I've tried it as both NSNumber and NSString.
<__NSArrayM 0xa01af70>(
800555,
800552,
800685,
803592,
803593
)
NSFetchRequest *request = [NSFetchRequest new];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"JournalHeader" inManagedObjectContext:[[BWCoreDataHelper sharedInstance] managedObjectContext]];
[request setEntity:entity];
[request setFetchBatchSize:20];
if( idsToList.count > 0 ) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"journalHeaderID IN %#", idsToList];
[request setPredicate:predicate];
}
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"headerCreateDate" ascending:NO];
[request setSortDescriptors:#[sort]];
NSFetchedResultsController *fetchedResults = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[[BWCoreDataHelper sharedInstance] managedObjectContext] sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController = fetchedResults;
self.fetchedResultsController.delegate = self;
From the lack of information on what results you are getting, or even if you get the delegate message, I have one suggestion.
Call - (void)performFetch:(NSFetchRequest *)fetch on the NSFetchedResultsController with your created fetch.
If you're already doing this, then it isn't obvious.

NSFetchedResultsController loads the whole data and bad performance

I have a large database with over 8000 entries in it. I want to load and list in tableview. I used NSFetchedResultsController. It's working fine except the bad performance. I found the tutorial here
And added my performFetch: method in the viewDidLoad: method. But it takes 2-3 seconds to load the view.
Here it is my code.
- (void)loadFetchResultsController {
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Error occurs while fetching the foods with fetch results controller %#, %#", error, [error userInfo]);
exit(-1);
}
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (isSearching)
{
[NSFetchedResultsController deleteCacheWithName:#"SearchResults"];
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Food" inManagedObjectContext:_managedObjectContext];
NSPredicate *hiddenPredicate = [NSPredicate predicateWithFormat:#"hidden == %#",[NSNumber numberWithBool:NO]];
if (isSearching)
{
hiddenPredicate = [NSPredicate predicateWithFormat:#"(hidden == %#) AND (name contains[cd] %# OR foodDescription contains[cd] %#)",[NSNumber numberWithBool:NO], searchTerm,searchTerm];
}
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:hiddenPredicate];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:_managedObjectContext sectionNameKeyPath:#"name.stringGroupByFirstInitial" cacheName:(isSearching) ? #"SearchResults" : #"Root"];
_fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
I used the instruments and found out that calling performFetch: method loads the whole data for the first time when viewDidLoad: calls.
My food data model is like this:
Any idea to improve the performance and get rid of delay.. Any help would be appreciated.
I believe you need to set the fetch limit on your fetch request, not just the batch size. Try calling [fetchRequest setFetchLimit:20]; and see if that helps. This should cause the first 20 records to be fetched. Then as the user scrolls through your data, make additional fetch requests to get more data.

Core Data fetchedresultscontroller returns empty on device only

I Wrote an application that uses core data and it was working fine, on both simulator and devices. Then I made a new git branch of the project and it works perfectly on the simulator but not on the devices.
Here is the code for the fetchedResultsController
-(NSFetchedResultsController *)fetchedResultsController{
if (_fetchedResultsController != nil) {
NSLog(#"Fetched Controler : %#", _fetchedResultsController);
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Membership" inManagedObjectContext:managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userdata.email == %#", [[NSUserDefaults standardUserDefaults] stringForKey:#"email"]];
NSSortDescriptor *sort1 = [[NSSortDescriptor alloc]initWithKey:#"type" ascending:NO];
NSSortDescriptor *sort2 = [[NSSortDescriptor alloc]initWithKey:#"membership_name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sort1,sort2, nil];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"type" cacheName:nil];
return _fetchedResultsController;
}
Then I fetch the objects in view will apear.
-(void)viewWillAppear:(BOOL)animated{
NSError *error;
[[self fetchedResultsController]performFetch:&error];
[self.tableView reloadData];
}
The managed object context is passed from appDelegate to the login view then from the loginView to the second view. On the second view is where I am having problems
The only thing that I can think of is that something is happening with the memory limitations on the device or maybe a concurrency issue?
If there are are any threads created I did not mean to do it.
It seems that your predicate
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"userdata.email == %#",
[[NSUserDefaults standardUserDefaults] stringForKey:#"email"]];
is returning an empty set. Log the value of this user default and I am convinced this will be cleared up.

Resources