Martin
Martin

Reputation: 321

UITableView not updating with Core Data

I have a Single View Application. It uses a ViewController embedded in a NavigationController. I have one UITextField and a UITableView added to the ViewController.

My model is extremely simple. It has two entities:

I am able to create a new item without issue. When I look at the SQLite database, it do see the actual data being inserted correctly. My issue right now is that I cannot seem to figure out why the data is not being displayed in my UITableView.

ViewController.m

#import "ViewController.h"

@interface ViewController () <NSFetchedResultsControllerDelegate>

@property (strong) NSMutableArray *items;

@property(nonatomic, strong) IBOutlet UITableView *tableView;
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;

@end

@implementation ViewController

- (void)viewDidLoad
{
[super viewDidLoad];

NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
    // Update to handle the error appropriately.
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    exit(-1);  // Fail
}
}


- (NSFetchedResultsController *)fetchedResultsController {

if (_fetchedResultsController != nil) {
    return _fetchedResultsController;
}

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:self.managedObjectContext];

[fetchRequest setEntity:entity];

NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"itemname" ascending:NO];

[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

[fetchRequest setFetchBatchSize:20];

NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"ItemCache"];

self.fetchedResultsController = theFetchedResultsController;

self.fetchedResultsController.delegate = self;

return _fetchedResultsController;

}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:@selector(managedObjectContext)]) {
    context = [delegate managedObjectContext];
}
return context;
}

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];

// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Item"];
self.items = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];

[self.tableView reloadData];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.items.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

// Configure the cell...
NSManagedObject *item = [self.items objectAtIndex:indexPath.row];
[cell.textLabel setText:[NSString stringWithFormat:@"%@", [item valueForKey:@"itemname"]]];
[cell.detailTextLabel setText:[item valueForKey:@"itemname"]];

return cell;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObjectContext *context = [self managedObjectContext];

if (editingStyle == UITableViewCellEditingStyleDelete) {
    // Delete object from database
    [context deleteObject:[self.items objectAtIndex:indexPath.row]];

    NSError *error = nil;
    if (![context save:&error]) {
        NSLog(@"Can't Delete! %@ %@", error, [error localizedDescription]);
        return;
    }

    // Remove device from table view
    [self.items removeObjectAtIndex:indexPath.row];
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}



- (IBAction)cancel:(id)sender {
//implement action here for cancel button to dismiss keyboard
}

- (IBAction)saveItem:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];

// Create a new managed object
NSManagedObject *newItem = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:context];
[newItem setValue:self.itemTextField.text forKey:@"itemname"];
NSLog(@"Creating new item");

NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
    NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"updateItem"]) {
    NSManagedObject *selectedItem = [self.items objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
    ViewController *destViewController = segue.destinationViewController;
    destViewController.itemTextField = selectedItem;
}
}

EDIT

Ive gone in and added my NSFetchedResultsController protocols.

- (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;
}
}

I seem to be getting an error on this line:

    case NSFetchedResultsChangeUpdate:
    [self configureCell:[tableView cellForRowAtIndexPath:indexPath]
            atIndexPath:indexPath];
    break;

Does this need a datasource declared before tableView? If the UITableView is connected to the File Owner's datasource, should this be self?

I think it should actually look like this:

    case NSFetchedResultsChangeUpdate:
    [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        break;

Upvotes: 0

Views: 735

Answers (2)

YSR fan
YSR fan

Reputation: 735

inside perform fetch block, implement [tableView reloadData];

Upvotes: 0

user4151918
user4151918

Reputation:

You need to implement NSFetchedResultsControllerDelegate to handle changes to results, such as when entities are inserted into the store.

The delegate will update the tableView's model.

At the least, you simply could reload the table when your delegate was called.

Upvotes: 1

Related Questions