Question

I'm missing something in my logic when trying to sync web service data with local store and I need your help. This is what I've got:

I have one NSArray of NSDictionaries describing each event object (downloaded from web), which I sort by event id. Then I fetch local store using IN predicate and also sort it by event id. Then I try to iterate and match the ids and if they match, i update record and if they don't match i create new NSManagedObject. It works fine if the newly downloaded event object has a greater eventID than last eventID in local store, but if the eventID from web service is smaller than the one in local store then it INSERTS ALL OBJECTS, no matter if they exist or not and that exactly is my problem.

So in other words, if a new record is at the beginning of sorted array it will add every object, but if it is at the end of sorted array it will update all except the new one. I need it to create the new one and update old ones.

Here's some code:

The function with the logic where I believe I'm missing something:

- (void)findOrCreateObject:(NSArray*)eventArray
{
    NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];

    //get sorted stored records
    NSArray *fetchedRecords = [self.fetchedResultsController fetchedObjects];

    //sort dictionaries
    NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"id" ascending:YES];
    NSArray *downloadedRecords = [self.events sortedArrayUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]];

    NSLog(@"DOWNLOADED EVENTS = %@", downloadedRecords);

    NSLog(@"FETCHED EVENTS = %@", fetchedRecords);


    //if store is not empty we need to walk through data and add/update records, otherwise/ELSE we need to import initial data
    if (fetchedRecords.count != 0) {
        //stores has records already
        NSLog(@"FIND OR CREATE PROCESS");

        if ([downloadedRecords count] > 0) {

            NSArray *storedRecords = [self fetchEvents:eventArray withContext:context];

            NSUInteger currentIndex = 0;
            for (NSDictionary* event in downloadedRecords) {
                Event* eventObject = nil;

                if ([storedRecords count] > currentIndex) {

                    eventObject = [storedRecords objectAtIndex:currentIndex];
                }

                NSLog(@"STRING VALUE OF KEY = %@", [[eventObject valueForKey:@"eventID"]stringValue]);

                if ([[event valueForKey:@"id"] isEqualToString:[[eventObject valueForKey:@"eventID"] stringValue]]) {

                    //Update Record
                    NSLog(@"Updating Record!!!");
                    [self updateManagedObject:eventObject withRecord:event inContext:context];

                }
                else
                {
                    //New Record
                    NSLog(@"Inserting Record!!!");
                    eventObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:context];
                    eventObject.eventID = [self makeNumberFromString:[event valueForKey:@"id"]];
                    eventObject.title = [event valueForKey:@"title"];
                    eventObject.venue = [event valueForKey:@"venue"];

                }
                 currentIndex++;
            }

        }

    }
    else
    {

        //import initial data
        NSLog(@"IMPORTING INITIAL DATA");

        for (NSDictionary* event in downloadedRecords) {

            Event *eventObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:context];

            eventObject.eventID = [self makeNumberFromString:[event valueForKey:@"id"]];
            eventObject.title = [event valueForKey:@"title"];
            eventObject.venue = [event valueForKey:@"venue"];

        }
    }


    // Save the context.
    NSError *error = nil;
    if (![context save:&error]) {

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

The FETCHEVENTS method:

-(NSArray*)fetchEvents:(NSArray*)eIDs withContext:(NSManagedObjectContext*)context
{

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

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(eventID IN %@)", eIDs];
    [fetchRequest setPredicate:predicate];

    [fetchRequest setSortDescriptors:@[ [[NSSortDescriptor alloc] initWithKey: @"eventID" ascending:YES] ]];

    NSError *error = nil;
    NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
    if (fetchedObjects == nil) {
        NSLog(@"No rows returned");
    }

    return fetchedObjects;
}

The Update Object method:

- (void)updateManagedObject:(NSManagedObject*)object withRecord:(NSDictionary*)record inContext:(NSManagedObjectContext*)context
{
    [object setValue:[self makeNumberFromString:[record valueForKey:@"id"]] forKey:@"eventID"];
    [object setValue:[record valueForKey:@"title"] forKey:@"title"];
    [object setValue:[record valueForKey:@"venue"] forKey:@"venue"];
}

I'm calling findOrCreate method once I download the web service data and parse it.

Let me know if you have any other questions.

Was it helpful?

Solution

Try this,

- (void)findOrCreateObject:(NSArray*)eventArray {

//if store is not empty we need to walk through data and add/update records, otherwise/ELSE we need to import initial data
    if (fetchedRecords.count != 0) {
        //stores has records already
        NSLog(@"FIND OR CREATE PROCESS");

        if ([downloadedRecords count] > 0) {

            NSArray *storedRecords = [self fetchEvents:eventArray withContext:context];
            for (NSDictionary* event in downloadedRecords) {
                NSPredicate *predicate = [NSPredicate predicateWithFormat:@"eventID = %@",[event valueForKey:@"id"]];
                NSArray *matchedArray = [storedRecords filteredArrayUsing
Predicate:predicate]; 
                Event* eventObject = nil;
                if ([matchedArray count] > 0) {

                    //Update Record
                    NSLog(@"Updating Record!!!");
                    eventObject = [matchedArray objectAtIndex:0];
                    [self updateManagedObject:eventObject withRecord:event inContext:context];

                }
                else
                {
                    //New Record
                    NSLog(@"Inserting Record!!!");
                    eventObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:context];
                    eventObject.eventID = [self makeNumberFromString:[event valueForKey:@"id"]];
                    eventObject.title = [event valueForKey:@"title"];
                    eventObject.venue = [event valueForKey:@"venue"];

                }
            }
        }
    } else {
        .....
    }
}

OTHER TIPS

I think, every time you insert a new event object, you should update storedObjects such that it should now contain the inserted object. Or more simply, you should put the initialisation line of storedObjects inside your for loop. (This would make sure that as you enumerate from the beginning of downloadedObjects every eventObject will have the same index on it as on storedObjects. But, with regards to your code this will only be true if all elements of storedObjects will always be found in downloadedObjects which, I assume is the case.)

One thing though, isn't fetchedRecords supposed to be the same as storedObjects, if they are you should just reassign storedObjects as [self.fetchedResultsController fetchedObjects], as it would reflect the changes in your context without executing another fetch request which would solve the inefficiency of the suggestion above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top