How to prevent Core Data making duplicates in iOS

2019-02-06 17:29发布

I've run into a problem.

Over the weekend I've been working on a project where I'm pulling a large xml from a webservice.

It basically has 3 tiers - Clients, Managers, Staff all hierarchical. So the first time the app runs, it pulls this xml and parses it and creates all the entries in the 3 releated Entities - Clients, Managers and Staff.

Every time the app launches I need to pull that same XML down, but this time, I only need to 'update' any of the existing records that have changed, or add new ones for new clients, managers or staff that have appeared since last time.

So - at the moment, as I said, it's pulling it all, parsing it correctly and creating the correct entities and filling in all the attributes.

However, with no data change, on the 2nd launch it's DUPLICATING all of the data - so instead of 15 clients ( the correct number ) I have 30 and so on...

Do I really have to add lots of code in my parsing to check that instead of creating a new NSManagedObject, I check if it's already there?

And if it is - I have to then manually check every attribute?

That's awfully painful and longwinded - isn't there a way to make Core Data do this kinda stuff for me - automatically?

Thanks for any help or suggestions.

2条回答
ゆ 、 Hurt°
2楼-- · 2019-02-06 17:54

I fear you have to keep your DB clean by yourself … The easiest way would be using NSFetchRequest: When importing your updated data you can run a query against the existing data and decide what you want to do.

As Marcus S. Zarra mentioned in another thread about this topic:

When you are importing a new row you can run a query against the existing rows to see if it is already in place. To do this you create a NSFetchRequest against your entity, set the predicate to look for the guid property and set the max rows returned to 1.

I would recommend keeping this NSFetchRequest around during your import so that you can reuse it while going through the import. If the NSFetchRequest returns a row you can update that row. If it does not return a row then you can insert a new row.

When done correctly you will find the performance more than acceptable.

Another source for good information are Apples Programming Guides: Core Data Programming Guide

查看更多
一夜七次
3楼-- · 2019-02-06 18:03

As Stated in Apple Docs https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html

You need to loop the data model and handle it from there like this

Example:

// loop over employeeIDs
// anID = ... each employeeID in turn
// within body of loop

NSString *predicateString = [NSString stringWithFormat: @"employeeID == %@", anID];

NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];

Personally I do not like this method and I wrote this snippet of code that handles this in a pro-efficient manor and which is straight forward! I noticed with Apples method I ran into issues with strings having different characters such as capitol letters and spaces. Below code is tested and working if you rename all your corresponding objects correctly I honestly believe this is the most efficient way to accomplish not adding duplicates in core data.

-(void)AvoidDuplicatesinDataModel
{
    // Define our table/entity to use
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Users"
                                              inManagedObjectContext:managedObjectContext];

    // Setup the fetch request
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entity];

    // Define how we will sort the records
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"users"
                                                                   ascending:NO];
    NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    [request setSortDescriptors:sortDescriptors];
    [sortDescriptor release];

    // Fetch the records and handle an error
    NSError *Fetcherror;
    NSMutableArray *mutableFetchResults = [[managedObjectContext
                                            executeFetchRequest:request error:&Fetcherror] mutableCopy];

    if (!mutableFetchResults) {
        // Handle the error.
        // This is a serious error
    }

    //here usersNameTextField.text can be any (id) string that you are searching for
    if ([[mutableFetchResults valueForKey:@"users"]
         containsObject:usernameTextField.text]) {
        //Alert user or handle your duplicate methods from here
        return;
    }
}
查看更多
登录 后发表回答