I been working in a project where i use core data to store some information, I have a UITableViewController with a UISegmentedControl and two NSFetchedResultsController because i need to fetched different data when i switch the segmented control.
When I first open the table view i can see the data corresponding to the first index of the segmented controller, but when i switch to the second index the data isn´t show and when i go back to the first index of the segmented control the data isn't show too.
This is the code
#import "TurnHistoryVC.h"
#import "AppDelegate.h"
#import "Turn.h"
#import "TurnInfoVC.h"
#import "Favorite.h"
#interface TurnHistoryVC ()
#property (nonatomic, retain) NSFetchedResultsController *currentFRC;
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, strong) NSFetchedResultsController *favoritesResultsController;
#end
#implementation TurnHistoryVC
#synthesize fetchedResultsController = _fetchedResultsController;
#synthesize favoritesResultsController = _favoritesResultsController;
- (void)viewWillAppear:(BOOL)animated{
NSInteger *turn_id = [[NSUserDefaults standardUserDefaults] integerForKey: #"turn_noti_id"];
if (turn_id != 0) {
[self performSegueWithIdentifier:#"myTurn" sender:self];
} else {
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
NSError *error;
self.currentFRC = self.fetchedResultsController;
if (![[self currentFRC] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
[self.tableView reloadData];
[super viewWillAppear:animated];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Atrás" style:UIBarButtonItemStylePlain target:nil action:nil];
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.currentFRC sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.currentFRC sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Turn *turn = [self.currentFRC objectAtIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"Turno no. %#", turn.turn_number];
cell.detailTextLabel.text = turn.companie_info;
cell.tag = [turn.turn_id intValue];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
// Display the authors' names as section headings.
// return #"";
return [[[self.currentFRC sections] objectAtIndex:section] name];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"cell id: %i", selectedCell.tag);
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
self.editButtonItem.title = #"Listo";
// [tableView ]
// Delete the managed object.
NSManagedObjectContext *context = [self.currentFRC managedObjectContext];
[context deleteObject:[self.currentFRC objectAtIndexPath:indexPath]];
NSError *error;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
// abort();
}
}
}
//Metodo para cambiar el texto del boton para borrar favoritos
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
return #"Elimiar";
}
-(void)toggleEdit{
[self.tableView setEditing:!self.tableView.editing animated:YES];
if (self.tableView.editing){
[self.navigationItem.leftBarButtonItem setTitle:#"Listo"];
}else{
[self.navigationItem.leftBarButtonItem setTitle:#"Editar"];
}
}
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([sender isKindOfClass:[UITableViewCell class]]){
if ([segue.destinationViewController isKindOfClass:[TurnInfoVC class]]){
TurnInfoVC *turnInfoVC = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
turnInfoVC.turn_id = selectedCell.tag;
}
}
}
#pragma mark - Cambio de turnos pendientes a finalizados
- (IBAction)btnTurnTypes:(UISegmentedControl *)sender {
NSError *error;
NSLog(#"cambie de tab 0");
switch (sender.selectedSegmentIndex) {
case 0:
{
self.currentFRC = self.fetchedResultsController;
[self.tableView reloadData];
}
break;
case 1:
{
self.currentFRC = self.favoritesResultsController;
[self.tableView reloadData];
NSLog(#"cambie de tab 1");
}
break;
default:
break;
}
[self.tableView reloadData];
}
#pragma mark - Fetched results controller
/*
Returns the fetched results controller. Creates and configures the controller if necessary.
*/
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
}
NSLog(#"turnos pendientes");
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Turn"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"turn_confirmation==0 AND turn_status==1"];
fetchRequest.predicate = predicate;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Turn" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *categoryDescriptor = [[NSSortDescriptor alloc] initWithKey:#"companie_info" ascending:YES];
NSSortDescriptor *companyDescriptor = [[NSSortDescriptor alloc] initWithKey:#"turn_number" ascending:YES];
NSArray *sortDescriptors = #[categoryDescriptor, companyDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"companie_info" cacheName:#"Root"];
_fetchedResultsController.delegate = self;
self.currentFRC = _fetchedResultsController;
return _fetchedResultsController;
}
- (NSFetchedResultsController *)favoritesResultsController {
if (_favoritesResultsController != nil) {
}
NSLog(#"turnos finalizados");
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Turn"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"turn_confirmation==0 AND turn_status==0"];
fetchRequest.predicate = predicate;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Turn" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *categoryDescriptor = [[NSSortDescriptor alloc] initWithKey:#"companie_info" ascending:YES];
NSSortDescriptor *companyDescriptor = [[NSSortDescriptor alloc] initWithKey:#"turn_number" ascending:YES];
NSArray *sortDescriptors = #[categoryDescriptor, companyDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
_favoritesResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"companie_info" cacheName:#"Root"];
_favoritesResultsController.delegate = self;
self.currentFRC = _favoritesResultsController;
return _favoritesResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
NSLog(#"actualizando fuera del if");
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
if (controller == self.currentFRC) {
NSLog(#"actualizando dentro del if");
[self.tableView beginUpdates];
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
#end
I already try this solution, but i can't get it to work, thanks
Related
I recently migrated a project from a self-managed object using a config singleton to use the NSManaged Object Context with NSFetchedResultController. What I'm trying to do is fill a TableView with sections that are based on month however the user can select a cell and change the month. when that happens it causes the following exception thrown and the cells become unable to change or edit
[error] error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to insert row 0 into section 1, but there are only 0 sections after the update with userInfo (null)
CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to insert row 0 into section 1, but there are only 0 sections after the update with userInfo (null)
Here is the main view controller fetch request:
- (NSFetchedResultsController<Budget *> *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
LogDebug(#"STARTED");
NSFetchRequest<Budget *> *fetchRequest = Budget.fetchRequest;
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"startTime" ascending:NO];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
NSFetchedResultsController<Budget *> *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"monthSection" cacheName:nil];
aFetchedResultsController.delegate = self;
NSError *error = nil;
if (![aFetchedResultsController performFetch:&error]) {
LogError(#"Unresolved error %#, %#", error, error.userInfo);
abort();
}
_fetchedResultsController = aFetchedResultsController;
return _fetchedResultsController;
}
Then for the Tableview Sections Data Source:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
NSInteger count = [sectionInfo numberOfObjects];
LogDebug(#"Number of Rows: %ld in Section %ld",(long)count, (long)section);
return [sectionInfo numberOfObjects];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger count = [[self.fetchedResultsController sections] count];
LogDebug(#"Sections: %ld",(long)count);
return [[self.fetchedResultsController sections] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
LogDebug(#"STARTED");
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController.sections objectAtIndex:section];
return [sectionInfo name];
}
I have also tried subclassing the data object model and added this to setting the month section:
- (NSString *)monthSection {
#synchronized (self.startTime) {
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:#"MMMM"];
NSString *sectionTitle = [formatter stringFromDate:self.startTime];
return sectionTitle;
}
}
Now when the user selects a table view cell I send the Budget Object to the DetailViewController by sending it the following way:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Budget *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
DetailViewController *controller = (DetailViewController *)[[segue destinationViewController] topViewController];
[controller setDetailItem:object];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
}
Then in DetailViewController i just use the setter on the startTime:
[startDatePicker setDate:detailItem.startTime];
[[AppDelegate instance] saveContext];
[self totalUpFields];
But once the user changes the Month date to something other than what was initially created it thrown that above exception.
I'm very new to the NSManaged Object structure and I've always used a managed config singleton.
For the changed object content here are the methods:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
default:
return;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] withBudget:anObject];
break;
case NSFetchedResultsChangeMove:
[tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
break;
}
}
Thanks for the help, let me know if i need to add additional details.
I believe this is an issue when moving the last row out of a section. You can resolve it by changing your case NSFetchedResultsChangeMove: to the following:
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
It looks like this happens because the sections are modified before the rows, so the index paths shift and the expected source/destination may no longer be valid.
It's also important to first sort by your sectionNameKeyPath:
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"monthSection" ascending:NO];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"startTime" ascending:NO];
[fetchRequest setSortDescriptors:#[sortDescriptor1, sortDescriptor2]];
You will need to add monthSection as a property on your Core Data model and set it when inserting/updating, instead of the dynamic method you're currently using in your subclass (otherwise you'll get an exception when performing the fetch).
I am currently attempting to make an iPhone app and have stumbled upon a little bump. I keep getting an error when using the NSFetchedResultsControllerDelegate and anywhere that I use the NSFetchedResults controller. As I am implementing Core Data in my code, it is essential I get this to work. Any feedback is welcome.
Here is the code:
ClassTableViewController.h
#import <UIKit/UIKit.h>
#import "Classes.h"
//#protocol NewClassViewControllerDelegate;
#interface NewClassViewController : UIViewController
//#property (weak, nonatomic) id <NewClassViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UITextField *classTextField;
#property (weak, nonatomic) IBOutlet UITextField *periodTextField;
#property (strong, nonatomic) Classes *classID;
- (IBAction)saveClass:(id)sender;
#end
ClassTableViewController.m
#import "ClassTableViewController.h"
#import "Classes.h"
#import "ClassTableViewCell.h"
#import "NewClassViewController.h"
#interface ClassTableViewController ()
- (void)configureCell:(ClassTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
#end
#implementation ClassTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
#pragma mark - Segue
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ( [[segue identifier] isEqualToString:#"ShowClass"] ){
NewClassViewController *dvc = (NewClassViewController *)[segue destinationViewController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Classes *classID = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[dvc setClassID:classID];
} else if ([[segue identifier] isEqualToString:#"addClass"]) {
NewClassViewController *dvc = (NewClassViewController *)[[segue destinationViewController] topViewController];
[dvc setClassID:[NSEntityDescription insertNewObjectForEntityForName:#"Classes" inManagedObjectContext:self.managedObjectContext]];
}
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(firstDone)];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void) firstDone {
[self.navigationController dismissModalViewControllerAnimated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ClassesCell";
ClassTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[ClassTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(ClassTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Classes *classID = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.classLabel.text = classID.classTitle;
cell.periodLabel.text = classID.period;
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Classes " inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"period" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
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;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
/*
#pragma mark - Navigation
// In a story board-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
Thanks for providing some code and asking for any feedback. The header file is confusing for me. I recommend you walk through the tutorial on this site.
Here's a snippet to get you started but walking through the code on the site is your best bet. Afterwards you can come back with specific questions and I will be happy to follow-up. If the tutorial is enough to get you going please feel free to accept this answer.
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"FailedBankInfo" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"details.closeDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
I have some "details" saved into my Core Data, and I'm trying to call it out from an NSFetchedResultsController into my tableView. For whatever reason, the tableView isn't populating once the block is finished running.
I have been searching and asking everywhere I can to try and figure out this whole Core Data debacle. Hopefully, someone on here is kind enough to help me out!
HomeViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = #"Home";
self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:0 green:0.7 blue:2.3 alpha:1];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:#selector(addShindy:)];
self.tableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"light_alu.png"]];
self.tableView.opaque = NO;
self.tableView.backgroundView = nil;
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refresh:)
forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
[self setShindyDatabase:self.shindyDatabase];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.shindyDatabase) {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
self.shindyDatabase = [[UIManagedDocument alloc] initWithFileURL:url];
}
}
- (void)refresh:(UIRefreshControl *)sender
{
[self useDocument];
[sender endRefreshing];
}
- (void)addShindy:(UIBarButtonItem *)sender
{
AddShindyViewController *addShindyViewController = [[AddShindyViewController alloc] initWithNibName:#"AddShindyViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addShindyViewController];
[self presentViewController:navController animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma Core Date Stack
- (void)fetchShindyDataIntoDocument:(UIManagedDocument *)document
{
dispatch_queue_t fetchIntoDocument = dispatch_queue_create("Fetch Into Document", nil);
dispatch_async(fetchIntoDocument, ^{
NSArray *shindys = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
for (NSDictionary *shindyInfo in shindys) {
[Shindy shindyWithShindyDBInfo:shindyInfo inManagedObjectContext:document.managedObjectContext];
}
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
});
}
- (void)setShindyDatabase:(UIManagedDocument *)shindyDatabase
{
if (_shindyDatabase != shindyDatabase) {
_shindyDatabase = shindyDatabase;
[self useDocument];
}
}
- (void)useDocument
{
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.shindyDatabase.fileURL path]]) {
NSLog(#"Create document");
[self.shindyDatabase saveToURL:self.shindyDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
[self setupFetchedResultsController];
[self fetchShindyDataIntoDocument:self.shindyDatabase];
if (!success) {
NSLog(#"error for creation of document: %#", [error localizedDescription]);
}
}];
} else if (self.shindyDatabase.documentState == UIDocumentStateClosed) {
NSLog(#"Closed document");
[self.shindyDatabase.managedObjectContext.parentContext performBlock:^{
[self setupFetchedResultsController];
}];
} else if (self.shindyDatabase.documentState == UIDocumentStateNormal) {
NSLog(#"Normal Document");
[self setupFetchedResultsController];
}
if (error) {
NSLog(#"Error in useDocument: %#", [error localizedDescription]);
}
}
- (void)setupFetchedResultsController
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Shindy"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"details" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
// request.predicate = [NSPredicate predicateWithFormat:#"details = %#", [self.shindyDatabase valueForKey:#"details"]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.shindyDatabase.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
}
#pragma mark - Table view data source
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 75;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
Shindy *shindy = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"%#", shindy.details);
cell.textLabel.text = shindy.details;
Shindy+CreateDB.m
+ (Shindy *)shindyWithShindyDBInfo:(NSDictionary *)shindyInfo
inManagedObjectContext:(NSManagedObjectContext *)context
{
Shindy *shindy = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Shindy"];
// NSSortDescriptor *dateAndTimeSort = [NSSortDescriptor sortDescriptorWithKey:#"dateAndTime" ascending:YES];
NSSortDescriptor *detailsSort = [NSSortDescriptor sortDescriptorWithKey:#"details" ascending:YES];
// NSSortDescriptor *locationSort = [NSSortDescriptor sortDescriptorWithKey:#"location" ascending:YES];
// NSSortDescriptor *nameSort = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
// NSSortDescriptor *photoSort = [NSSortDescriptor sortDescriptorWithKey:#"photo" ascending:YES];
// NSSortDescriptor *timePostedSort = [NSSortDescriptor sortDescriptorWithKey:#"timePosted" ascending:YES];
// title
request.sortDescriptors = [NSArray arrayWithObject:detailsSort];
// request.sortDescriptors = [NSArray arrayWithObjects:dateAndTimeSort, detailsSort, locationSort, nameSort, photoSort, timePostedSort, nil];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (error) {
NSLog(#"document failed in file: %#", [error localizedDescription]);
}
if (!matches || ([matches count] > 1)) {
NSError *error = nil;
NSLog(#"error in DBInfo: %#", [error localizedDescription]);
} else if ([matches count] == 0) {
shindy = [NSEntityDescription insertNewObjectForEntityForName:#"Shindy" inManagedObjectContext:context];
// shindy.dateAndTime = [shindyInfo objectForKey:#"dateAndTime"];
shindy.details = [shindyInfo objectForKey:#"details"];
// shindy.location = [shindyInfo objectForKey:#"location"];
// shindy.name = [shindyInfo objectForKey:#"name"];
// shindy.photo = [shindyInfo objectForKey:#"photo"];
// shindy.timePosted = [shindyInfo objectForKey:#"timePosted"];
// title
// Guestlist? The rest?
// Use below for reference
// shindy.whoseShindy = [User userWithName:[shindyInfo objectForKey:#"whoseShindy"] inManagedObjectContext:context];
} else {
shindy = [matches lastObject];
}
return shindy;
}
EDIT:
I suppose I should have also shown a file of which I have set as a subclass of my HomeViewController. It's basically the same thing as the code that is given to you to paste in the Apple Documentation
CoreDataTableViewController.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface CoreDataTableViewController : UITableViewController <NSFetchedResultsControllerDelegate>
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
- (void)performFetch;
#property (nonatomic) BOOL suspendAutomaticTrackingOfChangesInManagedObjectContext;
#property BOOL debug;
#end
CoreDataTableViewController.m
#interface CoreDataTableViewController()
#property (nonatomic) BOOL beganUpdates;
#end
#implementation CoreDataTableViewController
#pragma mark - Properties
#synthesize fetchedResultsController = _fetchedResultsController;
#synthesize suspendAutomaticTrackingOfChangesInManagedObjectContext = _suspendAutomaticTrackingOfChangesInManagedObjectContext;
#synthesize debug = _debug;
#synthesize beganUpdates = _beganUpdates;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#pragma mark - Fetching
- (void)performFetch
{
if (self.fetchedResultsController) {
if (self.fetchedResultsController.fetchRequest.predicate) {
if (self.debug) NSLog(#"[%# %#] fetching %# with predicate: %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate);
} else {
if (self.debug) NSLog(#"[%# %#] fetching all %# (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName);
}
NSError *error;
[self.fetchedResultsController performFetch:&error];
if (error) NSLog(#"[%# %#] %# (%#)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]);
} else {
if (self.debug) NSLog(#"[%# %#] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
}
[self.tableView reloadData];
}
- (void)setFetchedResultsController:(NSFetchedResultsController *)newfrc
{
NSFetchedResultsController *oldfrc = _fetchedResultsController;
if (newfrc != oldfrc) {
_fetchedResultsController = newfrc;
newfrc.delegate = self;
if ((!self.title || [self.title isEqualToString:oldfrc.fetchRequest.entity.name]) && (!self.navigationController || !self.navigationItem.title)) {
self.title = newfrc.fetchRequest.entity.name;
}
if (newfrc) {
if (self.debug) NSLog(#"[%# %#] %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), oldfrc ? #"updated" : #"set");
[self performFetch];
} else {
if (self.debug) NSLog(#"[%# %#] reset to nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
[self.tableView reloadData];
}
}
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [self.fetchedResultsController sectionIndexTitles];
}
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) {
[self.tableView beginUpdates];
self.beganUpdates = YES;
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
{
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.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
if (self.beganUpdates) [self.tableView endUpdates];
}
- (void)endSuspensionOfUpdatesDueToContextChanges
{
_suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
}
- (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend
{
if (suspend) {
_suspendAutomaticTrackingOfChangesInManagedObjectContext = YES;
} else {
[self performSelector:#selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:0];
}
}
#end
And then last, but not least, I have the header file for my HomeViewController of which I implement CoreDataTableViewController. Hopefully this is useful in better explaining what I've got.
HomeViewController.h
#import <UIKit/UIKit.h>
#import <FacebookSDK/FacebookSDK.h>
#import "CoreDataTableViewController.h"
#interface HomeViewController : CoreDataTableViewController
#property (strong, nonatomic) UIManagedDocument *shindyDatabase;
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
#end
It's quite difficult to see what is going on, but, I think you need to do the following steps when the block has finished (I suppose this since I don't see any similar in your code).
execute the fetch request through the fetched controller
reload the data table
So,
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
// error handling here...
}
[yourTable reloadData];
Try and let me know. If doesn't work, try to edit a minimal question because there is too much code to follow.
Hope that helps.
I ended up figuring this, and many other Core Data issues I was having. I wasn't setting my managed object context to a store!! DUH!!!
self.managedObjectContext = [(AppDelegate *)[UIApplication sharedApplication].delegate managedObjectContext];
That did the trick. Everything began working after that.
I hope this is able to point out another duh to anybody out there forgetting the same thing I did!
I'm having some problems implementing NSFetchedResultsController for something very simple. I'm just trying to learn how it works. All I want to do is display the firstName attribute of Person entities in a table view.
As far as I know I've implemented all the methods required but nothing is showing up in the table view. The method -tableView:cellForRowAtIndexPath: method isn't even being called.
Here is my code:
#implementation MyTableViewController
#synthesize managedObjectContext = _managedObjectContext;
#synthesize fetchedResultsController = _fetchedResultsController;
-(NSFetchedResultsController*) fetchedResultsController
{
if (!_fetchedResultsController)
{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Person"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"firstName" ascending:YES];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
}
return _fetchedResultsController;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Person *person = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = person.firstName;
return cell;
}
any help would be hugely appreciated! many thanks, Alex
You have to call [NSFetchedResultsController performFetch:] and implement its delegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray
arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray
arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
See more at: http://www.raywenderlich.com/999/core-data-tutorial-for-ios-how-to-use-nsfetchedresultscontroller
Try getting your NSString from your Person object and via the code here:
NSString *firstName = ((Person *)[personArray objectAtIndex:indexPath.row]).firstName;
Mind you, my suggested line is assuming that you output your NSFetchRequest to a NS(Mutable)Array called personArray.
Hopefully that helps, otherwise let me know.
I am getting a crash at line [selectedSession release]; in dealloc:
Default [NSCheapMutableString release]: message sent to deallocated instance
I don't understand why isn't this normal to put in dealloc?
All current code below:
LogViewController
#implementation LogViewController
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext;
#synthesize logArray;
#synthesize logTableView;
#synthesize imageView;
#synthesize session;
#synthesize selectedSession;
- (void)dealloc
{
[logArray release];
[logTableView release];
[session release];
[__fetchedResultsController release];
[managedObjectContext release];
[imageView release];
[selectedSession release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
self.logTableView.rowHeight = 47;
[super viewDidLoad];
self.navigationItem.title = #"Log";
logTableView.backgroundColor = [UIColor clearColor];
logTableView.separatorColor = [UIColor grayColor];
self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:24/255.0 green:83/255.0 blue:170/255.0 alpha:1.0];
self.logArray = [[NSArray alloc]initWithObjects:#"Today", #"Previous", #"Past Week", #"Past Month", #"All Workouts", nil];
if (managedObjectContext == nil)
{
self.managedObjectContext = [(CurlAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
}
- (void)viewDidUnload
{
self.logTableView = nil;
self.fetchedResultsController = nil;
self.imageView = nil;
self.managedObjectContext = nil;
[super viewDidUnload];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.logArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
TDBadgedCell *cell = [[[TDBadgedCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(TDBadgedCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
cell.textLabel.textColor = [UIColor blackColor];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [logArray objectAtIndex:indexPath.row];
cell.backgroundColor = [UIColor clearColor];
cell.imageView.image = [UIImage imageNamed:#"17-bar-chart.png"];
UIImageView *myImageView = nil;
myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"silvercell5.png"]];
[cell setBackgroundView:myImageView];
[myImageView release];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM d, y"];
NSDate *date = nil;
if (indexPath.row == 0)
{
date = [NSDate date];
NSString *dateString = [dateFormatter stringFromDate:date];
cell.badgeString = dateString;
}
else if (indexPath.row == 1)
{
if ([[self.fetchedResultsController fetchedObjects]count] > 1)
{
self.session = [[self.fetchedResultsController fetchedObjects]objectAtIndex:1];
NSDate *date = self.session.timeStamp;
NSString *dateString = [dateFormatter stringFromDate:date];
cell.badgeString = dateString;
}
else
{
cell.badgeString = #"None";
}
}
else if (indexPath.row > 1)
{
cell.badgeString = [NSString stringWithFormat:#"%i", [[self.fetchedResultsController fetchedObjects]count]];
}
cell.badgeColor = [UIColor colorWithRed:24/255.0 green:83/255.0 blue:170/255.0 alpha:1.0];
[dateFormatter release];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (indexPath.row == 0 || indexPath.row == 1)
{
SessionViewController *detailViewController = [[SessionViewController alloc] initWithNibName:#"SessionViewController" bundle:nil];
detailViewController.title = [logArray objectAtIndex: indexPath.row];
self.selectedSession = (Session *)[__fetchedResultsController objectAtIndexPath:indexPath];
detailViewController.selectedSession = self.selectedSession;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM d, y"];
NSString *dateString = [dateFormatter stringFromDate:selectedSession.timeStamp];
detailViewController.title = dateString;
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
[dateFormatter release];
}
else
{
LogResultsViewController *detailViewController = [[LogResultsViewController alloc] initWithNibName:#"LogResultsTableViewController" bundle:nil];
detailViewController.title = [logArray objectAtIndex: indexPath.row];
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController != nil)
{
return fetchedResultsController;
}
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Session" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0] ;
NSDate *today = [NSDate date];
NSDate *thisWeek = [today dateByAddingTimeInterval: -604800.0];
NSDate *thisMonth = [today dateByAddingTimeInterval: -2629743.83]; // Use NSCalendar for
if (indexPath.row ==2)
{
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"(date >= %#) AND (date <= %#)", thisWeek, today]];
}
else if (indexPath.row ==3)
{
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"(date >= %#) AND (date <= %#)", thisMonth, today]];
}
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
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:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"Number of Objects = %i",
[[fetchedResultsController fetchedObjects] count]);
return fetchedResultsController;
NSLog(#"Number of Objects = %i",
[[fetchedResultsController fetchedObjects] count]);
}
#pragma mark - Fetched results controller delegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.logTableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.logTableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.logTableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.logTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.logTableView endUpdates];
}
LogResultsViewController
#implementation LogResultsViewController
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext;
#synthesize resultsTableView;
#synthesize selectedSession;
- (void)dealloc
{
[__fetchedResultsController release];
[managedObjectContext release];
[selectedSession release];
[resultsTableView release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.resultsTableView.separatorColor = [UIColor grayColor];
self.resultsTableView.rowHeight = 50;
[self managedObjectContext];
}
- (NSManagedObjectContext *)managedObjectContext
{
if (managedObjectContext != nil)
{
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [(CurlAppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator];
if (coordinator != nil)
{
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return managedObjectContext;
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.managedObjectContext = nil;
self.fetchedResultsController = nil;
self.resultsTableView = nil;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Session *session = (Session *)[__fetchedResultsController objectAtIndexPath:indexPath];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"eeee, MMM d, y"];
NSString *dateString = [dateFormatter stringFromDate:session.timeStamp];
NSDate *lastDate = session.timeStamp;
NSDate *todaysDate = [NSDate date];
NSTimeInterval lastDiff = [lastDate timeIntervalSinceNow];
NSTimeInterval todaysDiff = [todaysDate timeIntervalSinceNow];
NSTimeInterval dateDiff = todaysDiff-lastDiff;
NSTimeInterval dayDifference = dateDiff/86400;
int days = (int) dayDifference;
NSLog(#"%i days",days);
cell.textLabel.text = dateString;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.detailTextLabel.text = [NSString stringWithFormat: #"%i days ago", days];
cell.detailTextLabel.textColor = [UIColor colorWithRed:24/255.0 green:83/255.0 blue:170/255.0 alpha:1.0];
cell.imageView.image = [UIImage imageNamed:#"11-clock.png"];
self.resultsTableView.tableFooterView = [[[UIView alloc] init] autorelease];
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"silvercell3.png"]];
[cell setBackgroundView:myImageView];
[dateFormatter release];
[myImageView release];
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the managed object for the given index path
NSManagedObjectContext *context = [__fetchedResultsController managedObjectContext];
[context deleteObject:[__fetchedResultsController objectAtIndexPath:indexPath]];
// Commit the change.
NSError *error = nil;
// Update the array and table view.
if (![managedObjectContext save:&error])
{
// Handle the error.
}
//[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
}
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SessionViewController *sessionViewController = [[SessionViewController alloc] initWithNibName:#"SessionViewController" bundle:nil];
selectedSession = (Session *)[__fetchedResultsController objectAtIndexPath:indexPath];
sessionViewController.selectedSession = self.selectedSession;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM d, y"];
NSString *dateString = [dateFormatter stringFromDate:selectedSession.timeStamp];
sessionViewController.title = dateString;
[self.navigationController pushViewController:sessionViewController animated:YES];
[sessionViewController release];
[dateFormatter release];
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController != nil)
{
return fetchedResultsController;
}
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Session" 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:#"timeStamp" ascending:NO];
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:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController;
}
#pragma mark - Fetched results controller delegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.resultsTableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.resultsTableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.resultsTableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.resultsTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.resultsTableView endUpdates];
}
SessionViewController
#implementation SessionViewController
#synthesize exerciseArray;
#synthesize selectedSession;
- (void)dealloc
{
[exerciseArray release];
[selectedSession release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
-(IBAction)showActionSheet {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Share" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Twitter",#"Facebook",nil];
[actionSheet showInView:self.tabBarController.view];
[actionSheet release];
}
- (void)tweet
{
SHKItem *aTweet = [SHKItem text:[NSString stringWithFormat: #"Twitter: testing 1,2,3."]];
[SHKTwitter shareItem:aTweet];
}
- (void)facebook
{
SHKItem *post = [SHKItem text: [NSString stringWithFormat: #"Facebook: testing 1,2,3."]];
// post.URL = [NSURL URLWithString:#"http://sugarrush-app.com/"];
[SHKFacebook shareItem:post];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem *actionButton = [[UIBarButtonItem alloc] initWithTitle:#"Share" style:UIBarButtonItemStylePlain target:self action:#selector(showActionSheet)];
self.navigationItem.rightBarButtonItem = actionButton;
[actionButton release];
NSSet *exercises = [self.selectedSession valueForKey:#"exercises"];
NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"timeStamp" ascending:YES]];
NSArray *sorted = [exercises sortedArrayUsingDescriptors:sortDescriptors];
self.exerciseArray = sorted;
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [exerciseArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
Exercise *exercise = (Exercise *)[exerciseArray objectAtIndex:indexPath.row];
cell.textLabel.text = exercise.name;
return cell;
}
#end
The only objects you should be releasing or setting to nil in viewDidUnload are like buttons, labels, textBoxes etc.
When you do self.selectedSession = nil, for example, it is calling the release for that method; that is the point of properties, they handle the allocs and releases.
You posted a tableView:didSelectRowAtIndexPath:, but I don't think you told us which class that was in.
In any case, that method seems to have a bug:
selectedSession = (Session *)[__fetchedResultsController objectAtIndexPath:indexPath];
sessionViewController.selectedSession = self.selectedSession;
I think on the first line, you meant to say self.selectedSession, assuming you go on to release selectedSession in this class' dealloc. (This may not be your only issue)
EDIT:
You should set properties to nil in viewDidUnload if and only if those properties were set as a result of the loading of the view from your nib, or were set in loadView, or in viewDidLoad. The reason you're doing this is because viewDidLoad or loadView may be called more than once during the lifetime of the view controller.
EDIT:
Check that you are using the property when setting fetchedResultsController and make sure you aren't setting that to nil on viewDidUnload.
Dont set selectedSession and exerciseArray to nil in viewDidUnload.
Since you're doing that, it will crash in the dealloc as you're trying to release nil objects.
Also, before you release arrays, it's always a good idea to call [array removeAllObjects].
in your viewDidLoad method you are setting self.selectedSession and self.exerciseArray to nil. You should only set interface outlets to nil in viewDidLoad. But this isn't the cause of your crash because by the time you get to dealloc, you are just sending release to nil.
you might want to look into the contents of the selectedSession Object. you might be over releasing an NSString in one of it's members