0

My data for for an entity's attribute is not being fetched upon restart or not being saved before quitting (could be either case). The bottom line is, the data is not showing up in the table upon restart. I think I may need to consolidate my core data methods or move them to the app delegate file instead.

I think my core data code is all messed up, can anyone help fix it? Let me know if you need any additional code to look at from the appdelegate.m etc. Here is the code for my View Controller:

#import "RoutineTableViewController.h"
#import "AlertPrompt.h"
#import "Routine.h"
#import "CurlAppDelegate.h"

@implementation RoutineTableViewController

@synthesize tableView;
@synthesize eventsArray;
@synthesize managedObjectContext;

- (void)dealloc
{
    [managedObjectContext release];
    [eventsArray release];
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

- (NSManagedObjectContext *) managedObjectContext {

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

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }
    return managedObjectContext;
}

-(void)addEvent
{
    Routine *routine = (Routine *)[NSEntityDescription insertNewObjectForEntityForName:@"Routine" inManagedObjectContext:managedObjectContext];

    CurlAppDelegate *curlAppDelegate = [[UIApplication sharedApplication] delegate];

    NSManagedObjectContext *context = [curlAppDelegate managedObjectContext];

    NSManagedObject *newRoutineEntry;

    newRoutineEntry = [NSEntityDescription insertNewObjectForEntityForName:@"Routine" inManagedObjectContext:context];


    NSError *error = nil;
    if (![managedObjectContext save:&error]) {
        // Handle the error.
    }

    [eventsArray insertObject:routine atIndex:0];
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    CurlAppDelegate *curlAppDelegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context = [curlAppDelegate managedObjectContext];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Routine" inManagedObjectContext:context];
    [request setEntity:entity];

    NSError *error = nil;
    NSMutableArray *mutableFetchResults = [[context executeFetchRequest:request error:&error] mutableCopy];
    if (mutableFetchResults == nil) {
        // Handle the error.
    }
    [self setEventsArray:mutableFetchResults];
    [mutableFetchResults release];
    [request release];

    UIBarButtonItem * addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(showPrompt)];
    [self.navigationItem setLeftBarButtonItem:addButton];
    [addButton release];

    UIBarButtonItem *editButton = [[UIBarButtonItem alloc]initWithTitle:@"Edit" style:UIBarButtonItemStyleBordered target:self action:@selector(toggleEdit)];
    self.navigationItem.rightBarButtonItem = editButton;
    [editButton release];

    [super viewDidLoad];
}

-(void)toggleEdit
{
    [self.tableView setEditing: !self.tableView.editing animated:YES];

    if (self.tableView.editing)
        [self.navigationItem.rightBarButtonItem setTitle:@"Done"];
    else
        [self.navigationItem.rightBarButtonItem setTitle:@"Edit"];
}

-(void)showPrompt
{
    AlertPrompt *prompt = [AlertPrompt alloc];
    prompt = [prompt initWithTitle:@"Add Workout Day" message:@"\n \n Please enter title for workout day" delegate:self cancelButtonTitle:@"Cancel" okButtonTitle:@"Add"];
    [prompt show];
    [prompt release];
}

- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if (buttonIndex != [alertView cancelButtonIndex])
    {
        NSString *entered = [(AlertPrompt *)alertView enteredText];
        if(eventsArray && entered)
        {
            [eventsArray addObject:entered];
            [tableView reloadData];
        }
    }
}

- (void)viewDidUnload
{
    self.eventsArray = nil;
    [super viewDidUnload];
}

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

- (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 [eventsArray count];
}

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellEditingStyleDelete reuseIdentifier:CellIdentifier] autorelease];
    cell.textLabel.text = [self.eventsArray objectAtIndex:indexPath.row];

    return cell;
}
// 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;
}

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
 {

     if (editingStyle == UITableViewCellEditingStyleDelete) {

         // Delete the managed object at the given index path.
         NSManagedObject *eventToDelete = [eventsArray objectAtIndex:indexPath.row];
         [managedObjectContext deleteObject:eventToDelete];

         // Update the array and table view.
         [eventsArray removeObjectAtIndex:indexPath.row];
         [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];

         // Commit the change.
         NSError *error = nil;
         if (![managedObjectContext save:&error]) {
             // Handle the error.
         }
     }
 }
@end

And here is the data model: enter image description here

Edit:

Added PersisentStoreCoordinater Method (From App Delegate):

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (__persistentStoreCoordinator != nil)
    {
        return __persistentStoreCoordinator;
    }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Curl.sqlite"];

    NSError *error = nil;
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&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.

         Typical reasons for an error here include:
         * The persistent store is not accessible;
         * The schema for the persistent store is incompatible with current managed object model.
         Check the error message to determine what the actual problem was.


         If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.

         If you encounter schema incompatibility errors during development, you can reduce their frequency by:
         * Simply deleting the existing store:
         [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]

         * Performing automatic lightweight migration by passing the following dictionary as the options parameter: 
         [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

         Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.

         */
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    

    return __persistentStoreCoordinator;
}
  • 1
    Can you please include your `persistentStoreCoordinator` method? It seems to be missing from your code. Also, are you purposely using two different managed object contexts? One in your view controller, and one in your delegate. – raidfive Mar 29 '11 at 02:24
  • I updated original question with persistentStoreCoordinator method which I got from my AppDelegate.m. Regarding the two different managed objects, I guess I messed this up, am I only supposed to have one in the app delegate? –  Mar 29 '11 at 02:33
  • I don't really see the need to have multiple declarations like that. Just reference those methods through your app delegate. You must also duplicate the `persistantStoreCoordinator` method in your view controller, otherwise `NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];` would toss an error. – raidfive Mar 29 '11 at 02:36
  • Ok, so add the `persistantStoreCoordinator` method I posted above into my viewController? And regarding the managed object context, I think you mean I have duplicated it in `addEvent` and `viewDidLoad` methods? Not app delegate vs viewController? –  Mar 29 '11 at 02:40

3 Answers3

0

My guess would be that your are mixing up your managed object contexts. Inserting into one and attempting to save on another. Remove your Core Data associated methods in your view controller and just try going through your app delegate methods. The data will either then be saved correctly, or an error will be tossed by Core Data. Make sure to inspect any setup/save errors in your Core Data code.

raidfive
  • 6,603
  • 1
  • 35
  • 32
  • Thanks raidfive, so are you suggesting that I delete `- (NSManagedObjectContext *) managedObjectContext` method from this viewController? Anything else I need to do? –  Mar 29 '11 at 02:44
  • Yea, then make sure you are going through your delegate when accessing the context `[appDelegate managedObjectContext]`. Let me know if that helps with your saving issue. – raidfive Mar 29 '11 at 03:42
  • Ok, ill try that. What bout in my `viewDidLoad` and my `addEvent` methods, I have `CurlAppDelegate *curlAppDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [curlAppDelegate managedObjectContext];` in both of them. Should I delete one of them, and which one? –  Mar 29 '11 at 03:47
  • Yes, that is the correct way to do it. Your `addEvent` method was using the method in your view controller though (not sure if that mattered). See `insertNewObjectForEntityForName:@"Routine" inManagedObjectContext:managedObjectContext];`. That `managedObjectContext` reference is actually calling that getter method I had you remove. – raidfive Mar 29 '11 at 03:53
  • @raidfive, thanks for the reply. I'm still a little confused though. I deleted the `- (NSManagedObjectContext *) managedObjectContext` method so far. What do I delete from addEvent? Just this `Routine *routine = (Routine *)[NSEntityDescription insertNewObjectForEntityForName:@"Routine" inManagedObjectContext:managedObjectContext]; CurlAppDelegate *curlAppDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [curlAppDelegate managedObjectContext];`? –  Mar 29 '11 at 03:56
  • Your `addEvent` should look something like this [gist](https://gist.github.com/3a411b9c9e424735960a). – raidfive Mar 29 '11 at 04:00
  • Thanks @raidfive, I fixed that. It is still crashing though, I think my viewDidLoad needs to be fixed, this is what I have now https://gist.github.com/891793 –  Mar 29 '11 at 04:08
  • The Core Data stuff looks fine. Had to tell without seeing your exact crash log. Run it through the debugger if it is unclear. – raidfive Mar 29 '11 at 04:17
  • That seems like a new, unrelated error. Try deleting the app completely from the Simulator and rebuilding. Or migrating the core data model like this [SO post](http://stackoverflow.com/questions/1018155/what-do-i-have-to-do-to-get-core-data-to-automatically-migrate-models). – raidfive Mar 29 '11 at 18:25
  • Here is my project in zip file (http://www.box.net/shared/static/uult48q64e.zip). I think I messed up somewhere with the code linking the RoutineViewController and the AppDelegate. Thanks. –  Mar 30 '11 at 00:39
0

Have you attached your core data file in your Main Bundle? There are two types of Bundle in iPhone app. Main Bundle and Application Bundle. We had a scenario something like yours. We copied the database into our main bundle and write the location of the database into our code. then it worked fine. you can check on this issue also.

Foyzul Karim
  • 4,252
  • 5
  • 47
  • 70
0

Here's a couple of tips to fix this :

  1. Remove all references to managedObjectContext, etc. in your RoutineViewController. Only reference the CoreData classes / properties from your AppDelegate.
  2. Whenever you need a reference (say in your AddEvent method) reference the appropriate objects from your delegate (see code below)
  3. When this gets more complex, you'll probably want a DataManager class that handles ALL your CoreData objects. Make it a Singleton, and you'll be able to reference it in any class. For fetching objects for display, you can then create the necessary methods in this class to retrieve these objects in a NSMutableArray

    CurlAppDelegate *curlAppDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [curlAppDelegate managedObjectContext];

    Routine *routine = (Routine *)[NSEntityDescription insertNewObjectForEntityForName:@"Routine" inManagedObjectContext:context];

See how you're now using the context from your AppDelegate and not locally? This is important, because your app will link your xdatamodel from your Bundle into your AppDelegate on initialization.

If you're concerned about the data not being saved, or overwritten, check out your AppDelegate's - (NSPersistentStoreCoordinator *)persistentStoreCoordinator

That's where the setup happens to link to your stored database when you exit and enter the app. Here's your code (modified) in your AppDelegate that loads in the Curl.sqlite file :

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{
if (__persistentStoreCoordinator != nil)
{
    return __persistentStoreCoordinator;
}

NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Curl.sqlite"];

NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{

    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}    

return __persistentStoreCoordinator;

}

Dominic Tancredi
  • 41,134
  • 7
  • 34
  • 50
  • Thanks @Dominic, I'm going to try this now. Also, I'm having an issue with the cell table I think it may be related. Question here:http://stackoverflow.com/questions/5482782/cell-not-being-added-to-tableviewcontroller –  Mar 30 '11 at 16:30
  • Ok so from my addEvent method, do I remove all of the following?: `CurlAppDelegate *curlAppDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [curlAppDelegate managedObjectContext]; Routine *routine = (Routine *)[NSEntityDescription insertNewObjectForEntityForName:@"Routine" inManagedObjectContext:context]; NSManagedObject *newRoutineEntry; newRoutineEntry = [NSEntityDescription insertNewObjectForEntityForName:@"Routine" inManagedObjectContext:context];` –  Mar 30 '11 at 19:59
  • No, that's fine. You're referencing your AppDelegate CoreData properties. So you're fine. – Dominic Tancredi Mar 30 '11 at 22:06
  • @Dominic, thanks. Im confused though, you said remove all references to managedObjectContext from my viewController. SO then wouldn't I remove that? –  Mar 31 '11 at 01:33
  • Well you still need to access your CoreData model... I said remove the references to your local "managedObjectContext". Now, you'll only need to use the "curlAppDelegate's managedObjectContext" - which is a SINGLE context that's binding to your CoreData model. You should only really use one reference if you can unless you want to manage mutiple models... – Dominic Tancredi Mar 31 '11 at 15:05
  • Also, with the code you posted in the comment, why are you calling insertNewObjectForEntityForName twice? You should just do the first one if you have that class defined. Don't really need the second. – Dominic Tancredi Mar 31 '11 at 15:06
  • And I'd be "more here" if y'all would accept my answer. This is the second question of yours I answered. :D – Dominic Tancredi Apr 01 '11 at 02:44
  • Sorry Dominic I was out of the country for a few days, I've just accepted your answer. Thanks. –  Apr 06 '11 at 06:19
  • So far, the only changes I have made from the code I have posted in the question is I have deleted the second insertNewObjectForEntityForName because I had it twice. But there is still more that needs to be done because it is not saving the data yet. For the add event, do I need something like this: `routine.name=@"Legs Day";`? –  Apr 06 '11 at 06:37
  • Yes, if you're not saving any properties, then it should insert a blank object. – Dominic Tancredi Apr 06 '11 at 13:55
  • Ok, so I think I need to add a line to save the property then, so if I add the line of code in my last comment and set it equal to what the user inputs in the UIAlert Prompt, then it should save to the name attribute string, correct? –  Apr 06 '11 at 14:20
  • Pretty much. Check also that you're saving the context! – Dominic Tancredi Apr 07 '11 at 04:04