ioscore-datansdictionarynsmutabledictionarynsset

Edit CoreData object then save context


I have two entities, one called InProject that has several attributes and one relationship. the relationship is with another entity called Ins.

I am editing one of the Ins that is related to InProject. I used InProject attribute ID which then returns a NSDictionary value that has several key-values one of which is for an array of Ins. I then find the Ins I need to edit in a for loop I edit them, but then I become unstuck because I am not sure how to save the contect of InProject with the *updated Ins

I need to figure out how to save InProject after I have overwritten the Ins attributes I need to update.

This is what my code looks like after battling this problem:

- (void)editSelectedins:(NSString *)projIDString UpdatedNSD:(NSMutableDictionary *)updatedNSD DPC:(int)dpc{

        // get context
        NSManagedObjectContext *context = [self managedObjectContext];

        if (context == nil) {
            NSLog(@"Nil");
        }
        else {
            NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
            NSEntityDescription *entity = [NSEntityDescription entityForName:@"InsProject" inManagedObjectContext:context];
            [fetchRequest setEntity:entity];

            NSError *error;
            NSMutableArray *InsProjectDictionaryArray = [[NSMutableArray alloc] init];
            NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];

            for (InsProject *insProj in fetchedObjects) {
                NSMutableDictionary *tempInsProjectDictionaryArray = [[ NSMutableDictionary alloc] init];

                [tempInsProjectDictionaryArray setObject:insProj.companyName forKey:@"CompanyName"];
                [tempInsProjectDictionaryArray setObject:insProj.projNo forKey:@"ProjNo"];
                [tempInsProjectDictionaryArray setObject:insProj.desc forKey:@"Desc"];
                [tempInsProjectDictionaryArray setObject:insProj.guid forKey:@"GUID"];
                [tempInsProjectDictionaryArray setObject:insProj.projID forKey:@"ProjID"];
                [tempInsProjectDictionaryArray setObject:insProj.ins forKey:@"ins"];

                [InsProjectDictionaryArray addObject:tempInsProjectDictionaryArray];
            }

            // now that you have the InsProjects, choose the one you are curently working on in insView using the projectID
            NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ProjID==%@",projIDString];
            [fetchRequest setPredicate:predicate];

            // new array with one value that was created using the NSPredicate ProjID
            NSArray *tempInsProjectArray = [InsProjectDictionaryArray filteredArrayUsingPredicate:predicate];

            // get ins array out of the NSDictionary to edit
            NSSet *inssForInsProject = tempInsProjectArray[0][@"ins"];
            NSMutableArray *tempAllinss = [[NSMutableArray alloc] init]; // this will contain everything, that means all repeated values are included

            for (Items* currItem in [inssForInsProject allObjects]) {
                NSArray *keys = [[[currItem entity] attributesByName] allKeys];
                NSDictionary *dict = [currItem dictionaryWithValuesForKeys:keys];
                [tempAllinss addObject:dict];
            }

            NSArray *myArray = [tempAllinss copy];

            // get the correct items from myArray anything whos dpc matches the dpc parameter of this method
            NSMutableArray *editedinsArray = [[NSMutableArray alloc] init];
            for (int i = 0; i < [myArray count]; i++) {
                NSMutableDictionary *tempinssDictionary = [myArray objectAtIndex:i];

                // if you get a match put it into the new editedinsArray to be edited
                if ([[tempinssDictionary objectForKey:@"dpc"] integerValue] == dpc) {
                    [editedinsArray addObject:tempinssDictionary];

                }
            }

            // by now you should have three things
            // 1, access to your ins coredata object //this s wrong I actually have access to insProject
            // 2, the values you need to be edited saved into a NSArray (editedinsArray, which will be used to check against and keep old values correct)
            // 3, UpdatedNSD which will be used to update any values that need to be updated.


            // go through your values and update the ins object
            int i = 0;
            for (ins *temp in editedinsArray) {
                NSDictionary *currentEditedins = [editedinsArray objectAtIndex:i];
                i++;

                // these values should stay the same so use currentEditedins which contains old vals
                NSString *stringToNumberDpc = [currentEditedins valueForKey:@"dpc"];
                int tempDpcNum = [stringToNumberDpc integerValue];
                NSNumber *dpcNumber = [NSNumber numberWithInt:tempDpcNum];
                temp.dpc = dpcNumber;

                NSString *totDQtyString = [currentEditedins valueForKey:@"totDQty"];
                if ((NSNull *)totDQtyString == [NSNull null]) {
                    temp.totDQty = @"";
                } else {
                    temp.totDQty = totDQtyString;
                }

                NSString *totShipString = [currentEditedins valueForKey:@"totShip"];
                if ((NSNull *)totShipString == [NSNull null]) {
                    temp.totShip = @"";
                } else {
                    temp.totShip = totShipString;
                }


                // values to be updated so use updatedNSD wthich was passed in as method param with the new vals
                temp.newInsComp = [updatedNSD valueForKey:@"newInsComp"];
                temp.newDryComp = [updatedNSD valueForKey:@"newDryComp"];
                temp.updatedRow = [updatedNSD valueForKey:@"updatedRow"];

            }
#warning --- I have no idea what to do here... i.e. how do I update the tempInsProjectArray.ins values I have just updated in the above for loop then save context which I hope would update insProj and the ins entities involved.         

            //save
            [context save:&error];


        }

}

As you can see at the bottom of the code with #warning I explain where I am having the issue. if I log temp inside the for loop I see the updated values perfectly the issue I am having is how do I then update the current tempInsProjectArray.ins values that I have just edited? then save them of course.


Solution

  • Your code is in great need of simplification. Some ground rules:

    1. Use names with smallInitial and camelCase for variables. So not InsProjectDictionaryArray but insProjectDictionaryArray.
    2. The same applies to dictionary keys indicating attribute names of managed objects. So projNo, not ProjNo.
    3. Avoid cryptic abbreviations. Use plain and readable English Not projNo but projectNumber. What is an Ins? What is "dcp"?
    4. Don't use the plural form for entity names. An suitable name for an item is Item, not Items
    5. Don't use the mutable versions of dictionary and array when immutable ones would do.
    6. Avoid duplicating your data, such as in [array copy].
    7. Avoid dictionaries when you have an object graph. The object graph is what core data creates. It renders dictionaries with values and keys unnecessary.
    8. Don't use IDs. The object graph renders those unnecessary as well in most cases. If you use IDs, do not use strings but numbers, such as long ints, or the object version NSNumber.
    9. When fetching data from the Core Data persistent store, don't fetch all the data and the filter the result. Fetch only the data you need.

    What you want to accomplish can surely be done in a few lines of code. I will try to summarize what you want to do as far as I understand it.

    Your data model looks something like this:

    Project <----->> Item
    

    Where the items are in a to-many relationship called ins. I will rename this items. I will also assume that you will refactor your IDs to be of type NSNumber.

    All the code up to myArray could be substituted with this:

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:"Project"];
    request.predicate = [NSPredicate predicateWithFormat:@"projectID = %@", projectID];
    request.fetchLimit = 1;
    NSArray *fetchedObjects = [self.managedObjectContext 
          executeFetchRequest:request error:nil]; 
    Project *project = fetchedObjects[0];
    

    You now have all items available simply with project.items. I understand that there could be more than one item with a mysterious attribute dcp of type int (i.e. NSNumber for managed objects), that is equal to the dcp parameter passed.

    NSSet *matchingItems = [project.items filteredSetUsingPredicate:
        [NSPredicate predicateWithFormat:@"dcp = %@", @(dcp)]];
    

    Now it becomes a bit murky. Why do you have type ins in your for loop if the ins are actually of type Item? You then cast them into a dictionary... This should generate a compiler error. Or you have another class called ins instead of Ins??

    Anyway, if you stay with the Items you can just update the values with what you pass in your dictionary:

    for (Item *item in matchingItems) {
      item.newInsComp = [updatedNSD valueForKey:@"newInsComp"];
      item.newDryComp = [updatedNSD valueForKey:@"newDryComp"];
      item.updatedRow = [updatedNSD valueForKey:@"updatedRow"];
    }
    
    [self.managedObjectContext save:nil];
    

    Done!

    BTW you could make it even shorter by setting the entity name of the fetch request to "Item" and setting the following predicate:

    [NSPredicate predicateWithFormat:@"project.projectID = %@ && dcp = %@", 
         projectID, @(dcp)];