CoreData model will always save an incorrect value for a Number type field

Discussion in 'iOS Programming' started by tutiplain, Jan 11, 2012.

  1. tutiplain, Jan 11, 2012
    Last edited: Jan 11, 2012

    tutiplain macrumors member

    Joined:
    Feb 4, 2011
    #1
    Hi all,

    In my most recent app project, I decided to go along and learn to use Core Data for storage. There is no reason I could not do it with plain old sqlite, I just wanted to try out the Core Data framework.

    I have, in my view controller, an UISegmentedControl with only two possible choices (labeled "Yes" and "No"). There is a save button connected in IB to the following method:

    Code:
    -(void)create_task
    {
        [txtDescription resignFirstResponder];
        
        CoreDataLayer *dl = self.dataLayer; //this is a wrapper class where my data context is stored
        
      
        NSManagedObjectContext* context = dl.context;
        NSEntityDescription* tareaDescription =[dl descriptionForEntityName:@"Task"];
        if (self.action ==@"new") 
        {
                    
            NSManagedObject* newTask = [[NSManagedObject alloc] initWithEntity:tareaDescription insertIntoManagedObjectContext:context];
            
            
            NSNumber *comp = [NSNumber numberWithInt:segCompleted.selectedSegmentIndex];
            
            [newTask setValue:txtDescription.text forKey:@"desc"];
            [newTask setValue:[dateAssignedControl date] forKey:@"date_assigned"];
            [newTask setValue:[dateCompControl date] forKey:@"date_completed"];
            [newTask setValue: comp forKey:@"completed"];
            [self.dataLayer.context insertObject:newTask];
            
        } 
        else 
        {
            [self.taskToEdit setValue:txtDescription.text forKey:@"desc"];
            [self.taskToEdit setValue:[dateAssignedControl date] forKey:@"date_assigned"];
            [self.taskToEdit setValue:[dateCompControl date] forKey:@"date_completed"];
            NSNumber * comp =[NSNumber numberWithInt:segCompleted.selectedSegmentIndex]; //this is the segmented control
            int x = [comp intValue];
            self.taskToEdit.completed = comp;
        }
        
        NSError *error;
        [dl.context save:&error];
        
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"Task has been saved." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert show];
        [self.navigationController popToRootViewControllerAnimated:YES];
        
    }
    
    If you will notice the else clause, I set the variable x to the value of the selected index of the UISegmentedControl. There are no error messages, but the value is always saved as 1 (I checked the .sqlite file used as the persistent store), even though x =0 (I checked by placing a breakpoint there) when the other segment is selected. Could someone help me decipher this behavior?


    -- edit ---

    Actually, when the else clause executes, no changes are saved at all, not even the other controls, nor the desc field. It works fine when self.action==@"new".
     
  2. RonC macrumors regular

    Joined:
    Oct 18, 2007
    Location:
    Chicago-area
    #2
    A handful of thoughts

    Zeroth comment: What is the value of self.taskToEdit and where does it get assigned

    (Why zero-based? Well, I thought of this one after I wrote all the other ones, but it's a very interesting one and I thought it should be at the top.)

    You say the @"new" one works and the else one doesn't. One key difference is what NSManagedObject is being impacted.

    First comment: Don't use == to compare strings, use isEqualToString:

    That == doesn't run compare: (at least AFAIK), it compares the pointer values. In general, I believe it is bad form to rely on the pointers being the same. It might work, but my sense is that it's an accident. You can tell me it's guaranteed and all that, but I don't care: I don't like it.

    Second comment: The debugger is your friend

    Run this code in the debugger, setting a breakpoint at the top of the if statement and step-over it one line at a time.

    Third comment: NSLog can be insightful

    Hey, you don't know WTF is going on, right, so drop some log statements in there too. You can see some interesting things when you log them, like values being nil.

    Fourth comment: Where is the subclass of NSManagedObject?

    I know you're just getting started, but you should generate the NSManagedObject subclasses for your entities and use them. Then you don't have to do this "setValue:forKey:" stuff, instead you just set the values as properties. All the CoreData magic is done for you.

    So let's say you had a NSManagedObject subclass named Task that has properties desc, date_assigned, date_completed, and completed. Your code would then look more like this:
    Code:
        if (self.action ==@"new") 
        {
            Task* newTask = [NSEntityDescription insertNewObjectForEntityForName:@"InstallRecord" 
                                                                              inManagedObjectContext:context];
    
            NSNumber *comp = [NSNumber numberWithInt:segCompleted.selectedSegmentIndex];
            
            newTask.desc = txtDescription.text;
            newTask.date_assigned = [dateAssignedControl date];
            newTask.date_completed = [dateCompControl date];
            newTask.completed = comp;
    //        [self.dataLayer.context insertObject:newTask]; // This is already inserted by the first statement
            
        } 
        else 
        {
            self.taskToEdit.desc = txtDescription.text;
            self.taskToEdit.date_assigned = [dateAssignedControl date];
            self.taskToEdit.date_completed = [dateCompControl date];
            NSNumber * comp =[NSNumber numberWithInt:segCompleted.selectedSegmentIndex]; //this is the segmented control
            int x = [comp intValue];
            self.taskToEdit.completed = comp;
        }
    
     
  3. bweberapps macrumors member

    Joined:
    Jun 25, 2010
    #3
    Try making it a type Boolean in your SQLite file and set it with NSNumber numberWithBool.
     
  4. tutiplain, Jan 12, 2012
    Last edited: Jan 12, 2012

    tutiplain thread starter macrumors member

    Joined:
    Feb 4, 2011
    #4
    Hi, thanks for your replies. In answer to your questions:

    self.taskToEdit is my NSManagedObject subclass. Its value gets assigned on the previous view, right before I do pushViewController.

    While not specified on the doc for UISegmentedControl, the selected index appears to be zero-based. Why? Because using my friend the debugger, I can see the value of x equal 0 when the segment on the left is selected, and equal to 1 when the segment on the right is selected.

    So far it's worked for me, but then again, I was not aware of the existence of isEqualToString. I will use it from now on.

    As mentioned above, I AM using the debugger. I thought I made this clear when in the original post when I mentioned I checked x's value by placing a breakpoint there.

    I tried this, too, and came to the same conclusion as when I tried it with debugger.


    You're right. When I originally wrote this code, I didn't know about NSManagedObject subclasses, that I can generate from the model. So I made the changes, specifically for the else clause, so now self.taskToEdit is a Task* object. After the original post, I also changed all the setValue:forKey method calls to property assignments, but I still had the same result.

    The reason why the @"new" clause is using a different object is simple: @"new" means I am creating a new object, while the else clause means I am simply updating an object that already exists in the data store. If I used newTask for editing, I would create a duplicate object within the data store.


    As for changing it to a Boolean, I will look into it, and report back.

    Thanks again to both for your thoughts and suggestions.


    -- edit --

    Debugger also shows me that the NSError variable is nil after the save method call.
     
  5. tutiplain thread starter macrumors member

    Joined:
    Feb 4, 2011
    #5
    Hi,

    Since I got no further replies, I spent a while trying to decipher this behavior myself, unsuccessfully. However, as I progressed in this project I discovered many flaws in my design, and basically had to restart almost from scratch. I've decided to drop all Core Data programming in this project. I've rewritten this particular view using the Sqlite 3 APIs, and with that, managed to get it to work properly. I don't know whether there is a bug somewhere in the Core Data framework or if it was probably something in my code that was causing the problem, but the bottom line is that it's solved now. I want to thank everyone who tried to help me in this.
     

Share This Page