iOS CoreData update entity object

11,697

In your header file, you should set up a mutable array for your cars.

NSMutableArray *carArray;

and

@property (nonatomic, retain) NSMutableArray *carArray;

Then make sure to synthesize it in your implementation file. Then when you fetch from your managed object context, you can set your array with the contents returned

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Car" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];

// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
if (mutableFetchResults == nil) {
    // Handle the error.
    NSLog(@"[AddCarViewController] deselect car: car not found.");
} else {
    [self setCarArray:mutableFetchResults];
}

Doing this will hold onto all the objects in your managed object context, so when you want to modify a Car, you can find it there instead of fetching again. If you need to sort it, you can apply a sort descriptor initialized with one of your attributes as a key. For example:

NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:@"selected" ascending:YES];
[carArray sortUsingDescriptors:[NSArray arrayWithObject:sorter]];

This will sort the Cars based on whether they are selected or not.

As for why you're getting an error, that could be one of many reasons. Typically the MOC will fail to save if one of your attributes is set to nil, so that might be the cause. You can get some detail from it if you set something like the following up

if (![self.managedObjectContext save:&error]) {
    NSLog(@"failed with error %@", error);
}

This will return the actual error you ran into. It would also be a good idea to set up a log to make sure you have Car specified. Something like NSLog(@"selected car %@", carToSave); just to be safe

Share:
11,697

Related videos on Youtube

Dachmt
Author by

Dachmt

Living the dream in San Francisco, CA, from south west of France, loving surfing, outdoors and developing websites.

Updated on June 04, 2022

Comments

  • Dachmt
    Dachmt almost 2 years

    I'm implementing a car manager for an iPhone app and I have trouble saving a new car. So I have a "Car" entity (and per-loaded DB), containing multiples attributes. I have 2 booleans "saved" and "selected" to track which car the user added to his list (if added, saved = 1) and which car is currently selected. So when I create a new car, I deselect the old one (selected=0), and want to modify the new car to set its attributes saved=1 and selected=1.

    Here is my functions:

    - (IBAction)save
    {
        // Disable the previous car selection.
        [self deselectCurrentSelectedCar];
    
        // Add new car as saved and selected.
        [self saveAndSelectNewCar];
    
        // Call the delegate to dismiss the modal view.
        [_delegate dismissAndSave];
    }
    
    - (void)deselectCurrentSelectedCar
    {    
        // Fetched saved car.
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Car" inManagedObjectContext:[self managedObjectContext]];
        [fetchRequest setEntity:entity];
    
        // Set predicate and sort orderings...
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"selected = 1"];
        [fetchRequest setPredicate:predicate];
    
        // Execute the fetch -- create a mutable copy of the result.
        NSError *error = nil;
        NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
        if (mutableFetchResults == nil) {
            // Handle the error.
            NSLog(@"[AddCarViewController] deselect car: car not found.");
        }
        else {
            // Get car and assign new selected value.
            Car *carToSave = (Car *)[mutableFetchResults objectAtIndex:0];
            [carToSave setSelected:[NSNumber numberWithInt:0]];
    
            // Save the car.
            NSError *error = nil;
            if (![self.managedObjectContext save:&error]) {
                // Handle the error.
                NSLog(@"[AddCarViewController] deselect car: error saving car.");
            }
            else {
                NSLog(@"[AddCarViewController] deselect car: car saved.");
            }
        }
    
        // Memory management.
        [fetchRequest release];
        [mutableFetchResults release];
    }
    - (void)saveAndSelectNewCar
    {
        // Get the car, and pass to the delegate the new settings.
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Car" inManagedObjectContext:[self managedObjectContext]];
        [fetchRequest setEntity:entity];
    
        // Set predicate and sort orderings...
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(year=%@) AND (make=%@) AND (model=%@) AND (d=%@) AND (n=%@)", _car.year, _car.make, _car.model, _car.d, _car.n];
        [fetchRequest setPredicate:predicate];
    
        // Execute the fetch -- create a mutable copy of the result.
        NSError *error = nil;
        NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
        if (mutableFetchResults == nil) {
            // Handle error.
            NSLog(@"[AddCarViewController] save and select: can't save the new car");
        }
        else {
            // Get the car selected.
            Car *selectedCar = (Car *)[mutableFetchResults objectAtIndex:0];
            [selectedCar setValue:[NSNumber numberWithInt:1] forKey:@"selected"];
            [selectedCar setValue:[NSNumber numberWithInt:1] forKey:@"saved"];
    
            // Save the car.
            NSError *error = nil;
            if (![self.managedObjectContext save:&error]) {
                // Handle the error.
                NSLog(@"[AddCarViewController] save and select: error saving car.");
            }
            else {
                NSLog(@"[AddCarViewController] save and select: car saved.");
            }
    
            // Add car to delegate.
            [EcoAppAppDelegate setUserCar:selectedCar];
        }
    
        // Memory management.
        [fetchRequest release];
        [mutableFetchResults release];
    }
    

    And I have this log all the time: "error saving car." on both functions. So there is definitely something wrong.

    Also, it's pretty anoying to fetch the car I want to modify it, instead of doing right away an update. If there another please tell me!

    Thanks.

    • Srivathsalachary Vangeepuram
      Srivathsalachary Vangeepuram almost 13 years
      You need to provide more information. You have two NSLog statements that print "error saving car". Which one are you getting the error from? And when this happens, what does the NSError parameter have to say about it? That parameter is there so Core Data can tell you what went wrong, so it's the first thing you should be looking at.
  • Dachmt
    Dachmt almost 13 years
    I am using a predicate because I preload my SQLite DB and insert more than 10,000 cars in it. So I don't want to fetch everything. Then I tried to log the car, and I can see the car, and even the changes have been made, etc. So it's like half working I guess, but on the device it crashes, not on the simulator. Something I find weird is that I have an attribute "epaClass" type String, and its value is sometimes nil, even if when I load my DB none of any of the attributes are NULL.
  • Dachmt
    Dachmt almost 13 years
    This attribute "epaClass" just does not make sense, it is supposed to be a string all the time, there is never a NULL value so something's wrong with that. And maybe trying to add a nil value when updating my object generates this error.
  • justin
    justin almost 13 years
    I can definitely understand using a predicate if you are loading that many cars. It still might be a good idea to set them to a local array though (after fetching with the predicate, of course), then modify them from there. I've had issues in the past where strings set to nil have crashed my app on a save, so that could be the problem, though the only way to make sure is to test it. Possibly set them up with a default value so at least something is present. But you say it works on the simulator, just not on the device?
  • Dachmt
    Dachmt almost 13 years
    I found the problem. I was using a Car as an object to store the parameter the user is choosing in order to store the 5 attributes that is my primary key (model, year, etc), so my object was empty except for those 5 attributes. I wanted to use my entity as an object, so when saving the context to commit the change, it was trying my updated object (that's why changes were working), and the other Car object that I used to get from the user the 5 main attributes. It might be confusing what I'm saying, don't really know how to explain it without all the code.
  • Dachmt
    Dachmt almost 13 years
    But basically, when saving the context it was saving my fetched/modified entity (so good, what I wanted), and another entity that was almost all empty that I was using for letting the user choose his car. My question is can I use my Car class (generated from the entity) as a basic object? And thanks for your help @slev!
  • justin
    justin almost 13 years
    I'm glad you figured that part out. And I understand what you're saying, so it's all good. But what do you mean by wanting to use the entity class as an object? I sort of understand what you mean, but without a bit more detail, it's hard to say for sure whether what you want to do is possible. Do you want to use it as a blank template?