Handling duplicate entries in Core Data

前端 未结 6 488
野的像风
野的像风 2021-02-04 10:28

I have an app that allows users to save favorites. I am using Core Data to store the favorites as managed objects. I have written some code to prevent the possibility of storing

相关标签:
6条回答
  • 2021-02-04 10:32

    I was wondering if Core Data has the ability to check to see if an object being added is a duplicate.

    No, Core Data doesn't care about that.

    Is there a predicate that can handle checking for duplicates?

    Since your objects have unique IDs that you control, do a fetch for an existing favorite with that ID. Something like

    NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Favorite"];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"stationIdentifier == %@", stID];
    [fetch setPredicate:predicate];
    

    If you get any results, you know that a favorite with that ID already exists. And, you have a reference to that favorite in case you want to change it.

    Your current approach is fine and probably faster if there are only a few favorites. Doing a fetch will scale better to lots of favorites.

    0 讨论(0)
  • 2021-02-04 10:35

    Just an update since iOS 9.0 you can do it easily with "unique constraints" in the model. But pay attention if your store already contains duplicate core data will fail any auto migration when the app shipped.

    See here for example - core data unique constraints

    0 讨论(0)
  • 2021-02-04 10:39

    Swift 3:

    func isExist(id: Int) -> Bool {
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: myEntityName)
        fetchRequest.predicate = NSPredicate(format: "id = %d", argumentArray: id)
    
        let res = try! theContext.fetch(fetchRequest)
        return res.count > 0 ? true : false
    }
    
    0 讨论(0)
  • 2021-02-04 10:42

    CoreData does no uniquing by itself. It has no notion of two entries being identical.

    To enable such a behavior you have to implement it yourself by doing a 'search before insert' aka a 'fetch before create'.

    NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Favorite"];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"stationIdentifier == %@", stID];
    [fetch setPredicate:predicate];
    YourObject *obj = [ctx executeRequest:fetch];
    
    if(!obj) {
        //not there so create it and save
        obj = [ctx insertNewManagedObjectForEntity:@"Favorite"]; //typed inline, dont know actual method
        obj.stationIdentifier = stID;
        [ctx save];
    }
    
    //use obj... e.g.
    NSLog(@"%@", obj.stationIdentifier);
    

    Remember this assumes single-threaded access

    0 讨论(0)
  • 2021-02-04 10:44

    Swift 4 Curled from @Vahid answer

    func isEntityAttributeExist(id: Int, entityName: String) -> Bool {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        let managedContext = appDelegate.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
        fetchRequest.predicate = NSPredicate(format: "id == %@", id)
        let res = try! managedContext.fetch(fetchRequest)
        return res.count > 0 ? true : false
    }
    
    0 讨论(0)
  • 2021-02-04 10:48

    If you're dealing with multiple records, iterating over a count fetch or retrieving actual objects is VERY costly on the CPU. In my case, I did one fetch for all matching records, but asked for a dictionary of just the string of the UUID back. Saves a lot of CPU overhead.

    For example, I have a uUID property on every record in core data. I have a corresponding UUID listed as @"UUID" in CloudKit.

      //1. Create a request for the entity type, returning an array of dictionaries  
          NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:@"someEntityName"];
          [request setResultType:NSDictionaryResultType];
          [request setReturnsDistinctResults:YES];
          [request setPropertiesToFetch: @[@"uUID"]];
    
      //2. Create an array of UUID strings of the downloaded objects
          NSMutableArray *UUIDstrings = [NSMutableArray new];
          for (CKRecord *record in ckRecords) {
            [UUIDstrings addObject:record[@"UUID"]];
          }
    
       //3. Create a predicate to find any Core Data objects with the same UUID
          [request setPredicate:[NSPredicate predicateWithFormat:@"uUID in %@", UUIDstrings]];
    
       //4. If there are results from the fetch, do a log and you'll see it's a dictionary. 
          NSArray *deck = [self.MOC executeFetchRequest:request error:nil];
    
          NSLog(@"Logging the result of index 0. Should be a dictionary %@", deck.count > 0 ? [deck objectAtIndex:0] : @"No results");
    
       //5. Then either do an embedded fast enumeration (for xx in xx){for xx in xx} to find a match like         
    
               if ([(NSString *)record[@"UUID"] isEqualToString:[dict valueForKey:@"uUID"]]) 
              {do something}
    
       //...Or 6. Use a more linear approach with NSSet
    
        //Harvest the core data strings
          NSMutableArray *coreDataStrings = [NSMutableArray new];
            for (NSDictionary *dict in deck) {
            [coreDataStrings addObject:[dict objectForKey:@"uUID"]];
          }
    
       //Create a set of your downloaded objects
          NSSet *arraySet = [NSSet setWithArray:ckRecords];
    
       //Then use a predicate search - a NOT version of above
         NSArray *final = [[arraySet filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"NOT(UUID in %@)", coreDataStrings]]allObjects];
    

    The console log of the dictionary will look something like this. Just the smallest amount of info required to match:

    dictionary {
       uUID = "AFACB8CE-B29E-4A03-9284-4BD5F5464";
    }
    

    More here at the developer site on finding unique values.

    0 讨论(0)
提交回复
热议问题