0

My Data Model is named "Person" and it has 3 attributes "id", "firstName", and "lastName"

When importing the JSON data using AFNetworking I want to be able to check whether the entity already exists or not within Core Data using the "id" as the identifier. If it isn't there I would like to create it, and if it is there I would like to merge the item or update it.

right now I have a method called duplicateCheck which looks like:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id==%@", _person.id];
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSError *error = nil;
[fetch setEntity:[NSEntityDescription entityForName:@"Person" inManagedObjectContext:self.managedObjectContext]];
[fetch setPredicate:predicate];
NSArray *items = [self.managedObjectContext executeFetchRequest:fetch error:&error];
for (NSManagedObject *object in items) {
// Not sure how to check from here and insert or update

// then save and call it during the API request?
}

I have a predicate set up but am not sure where to go from here. Is looping over each item the right way to go or am I going about this the wrong way?

2
  • You can use a loop but your request should return only 1 object, otherwise you have a data inconsistency and multiple objects with the same id. Commented May 14, 2013 at 18:21
  • Shameless plug, but this may be of help to you. raywenderlich.com/15916/… Commented May 14, 2013 at 19:05

2 Answers 2

0

Usually one would expect an identifier to be unique. therefor if the predicate return 0 objects, you know that this object is new. If 1 is returned you know that this object already exists and maybe you need to update it.

NSArray *items = [self.managedObjectContext executeFetchRequest:fetch error:&error];
if(items){ 
    if([items count] == 0){
        //the object is not present yet. create it.
    } else if([items count] == 1) {
        NSManageObject *obj = items[0];
        //there is exactly 1 object. change it properties if needed 
    } else {
        //what to do if several objects have the same identifier???
    }
} else {
    //handle error from the error object
}
Sign up to request clarification or add additional context in comments.

12 Comments

The data that's coming in has unique id's for all the "Person" objects. So I won't have to worry about objects having the same identifier.
So what is your question then?
Sorry forgot to add it. If there are multiple objects that need to be merged/updated, how do I go about doing that? Reading the code above it seems like only one object is being added and /or updated.
you do it one after the other. you could loop over the dictionries representing the objetcts in the response and add all those identifiers to an array and fetch all the objects at once by preating a predicate [NSPredicate predicateWithFormat:@"id IN %@", idArray], but you will have to touch every object to see if it needs to be created or altered.
@ikinciviking: From developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…: "Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object."
|
0

So I commented with a link to a tutorial I wrote on this topic, but to narrow it down, this method may help guide you.

  NSManagedObjectContext *managedObjectContext = [[SDCoreDataController sharedInstance] backgroundManagedObjectContext];
    //
    // Iterate over all registered classes to sync
    //
    for (NSString *className in self.registeredClassesToSync) {
        if (![self initialSyncComplete]) { // import all downloaded data to Core Data for initial sync
            //
            // If this is the initial sync then the logic is pretty simple, you will fetch the JSON data from disk 
            // for the class of the current iteration and create new NSManagedObjects for each record
            //
            NSDictionary *JSONDictionary = [self JSONDictionaryForClassWithName:className];
            NSArray *records = [JSONDictionary objectForKey:@"results"];
            for (NSDictionary *record in records) {
                [self newManagedObjectWithClassName:className forRecord:record];
            }
        } else {
            //
            // Otherwise you need to do some more logic to determine if the record is new or has been updated. 
            // First get the downloaded records from the JSON response, verify there is at least one object in 
            // the data, and then fetch all records stored in Core Data whose objectId matches those from the JSON response.
            //
            NSArray *downloadedRecords = [self JSONDataRecordsForClass:className sortedByKey:@"objectId"];
            if ([downloadedRecords lastObject]) {
                //
                // Now you have a set of objects from the remote service and all of the matching objects 
                // (based on objectId) from your Core Data store. Iterate over all of the downloaded records 
                // from the remote service.
                //
                NSArray *storedRecords = [self managedObjectsForClass:className sortedByKey:@"objectId" usingArrayOfIds:[downloadedRecords valueForKey:@"objectId"] inArrayOfIds:YES];
                int currentIndex = 0;
                // 
                // If the number of records in your Core Data store is less than the currentIndex, you know that 
                // you have a potential match between the downloaded records and stored records because you sorted 
                // both lists by objectId, this means that an update has come in from the remote service
                //
                for (NSDictionary *record in downloadedRecords) {
                    NSManagedObject *storedManagedObject = nil;
                    if ([storedRecords count] > currentIndex) {
                        //
                        // Do a quick spot check to validate the objectIds in fact do match, if they do update the stored 
                        // object with the values received from the remote service
                        //
                        storedManagedObject = [storedRecords objectAtIndex:currentIndex];
                    }

                    if ([[storedManagedObject valueForKey:@"objectId"] isEqualToString:[record valueForKey:@"objectId"]]) {
                        //
                        // Otherwise you have a new object coming in from your remote service so create a new 
                        // NSManagedObject to represent this remote object locally
                        //
                        [self updateManagedObject:[storedRecords objectAtIndex:currentIndex] withRecord:record];
                    } else {
                        [self newManagedObjectWithClassName:className forRecord:record];
                    }
                    currentIndex++;
                }
            }
        }
        //
        // Once all NSManagedObjects are created in your context you can save the context to persist the objects 
        // to your persistent store. In this case though you used an NSManagedObjectContext who has a parent context 
        // so all changes will be pushed to the parent context
        //
        [managedObjectContext performBlockAndWait:^{
            NSError *error = nil;
            if (![managedObjectContext save:&error]) {
                NSLog(@"Unable to save context for class %@", className);
            }
        }];

        //
        // You are now done with the downloaded JSON responses so you can delete them to clean up after yourself, 
        // then call your -executeSyncCompletedOperations to save off your master context and set the 
        // syncInProgress flag to NO
        //
        [self deleteJSONDataRecordsForClassWithName:className];
        [self executeSyncCompletedOperations];
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.