I have thousands of points in my Core Data database and I would like to have all them in my tableView but I don't want to fetch them to memory all at once but I would like to use NSFetchedResultsController
and batch them with some constant. I set it like I saw in many tutorials and I set fetchBatchSize property but It took several seconds to load view controller before it is shown.
This function is called in viewDidLoad:
- (void)initPositionsFetch {
// Initialize Fetch Request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"OpenPositionCD"];
// Add Sort Descriptors
[fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"timestamp" ascending:NO]]];
// Add predicate
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"moduleId == %@", _module.moduleId]];
[fetchRequest setFetchBatchSize:100];
[fetchRequest setIncludesPendingChanges:NO];
// Initialize Fetched Results Controller
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:temporaryContext sectionNameKeyPath:nil cacheName:nil];
// Configure Fetched Results Controller
[self.fetchedResultsController setDelegate:self];
// Perform Fetch
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) {
NSLog(@"Unable to perform fetch.");
NSLog(@"%@, %@", error, error.localizedDescription);
}
}
NSFetchedResultsControllerDelegate methods:
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
switch (type) {
case NSFetchedResultsChangeInsert: {
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeDelete: {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeUpdate: {
[self configureCell:(PanelPositionCell *)[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
}
case NSFetchedResultsChangeMove: {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
UITableView delegate methods:
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (_tracksVisible)
return 1;
else
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (_tracksVisible)
return [_tracks count];
else {
NSArray *sections = [self.fetchedResultsController sections];
id<NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier;
if(_tracksVisible)
CellIdentifier = TrackCellIdentifier;
else
CellIdentifier = PositionCellIdentifier;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(_tracksVisible) {
[((PanelTrackCell *)cell) setValuesByTrackCD:[_tracks objectAtIndex:indexPath.row]];
} else {
[self configureCell:(PanelPositionCell *)cell atIndexPath:indexPath];
}
return cell;
}
- (void)configureCell:(PanelPositionCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Fetch Record
NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
OpenPositionCD *position = (OpenPositionCD *)record;
// Update Cell
[cell setValuesByOpenPositionCD:position];
}
What is problem with this? Why it's loading all rows? Could be problem with Core Data model? I've tried to remove predicate and it doesn't help. SortDescriptor must be set but it's only thing what came to my mind where could be problem. I want result to be sort by timestamp. Timestamp is Date property and is set to be Indexed. How can results be batched when first I must get it and then I can sort them by time? It shouldn't be problem that I am using tableView for showing another data by switch (_tracksVisible), right?
I even tried to fetch results in background with this code:
[[[self fetchedResultsController] managedObjectContext] performBlock:^{
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) {
NSLog(@"Unable to perform fetch.");
NSLog(@"%@, %@", error, error.localizedDescription);
}
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[_tableView reloadData];
}];
}];
What happened was that controller was shown much earlier but then UI freeze and it takes almost same time as before.