8

I have an entity in Core Data which has an attribute that needs to be unique. There's no way to set this in the visual interface. I assume I need to create a custom class that inherits from NSManagedObject and then write my own validation method.

I successfully created the custom class by selecting the entities in the visual editor and choosing File -> New -> New File -> NSManagedObject subclass. I use this to add creation timestamps, so I know it works.

But now what? Which methods do I need?

The NSManagedObject reference guide tells me to "implement methods of the form validate:error:" but doesn't provide an example.

Similar questions here and here, but I need a bit more help.

A complete example would be awesome, but any help is much appreciated.

Community
  • 1
  • 1
Sjors Provoost
  • 1,921
  • 1
  • 19
  • 34
  • Did you try either of the solutions in the answer they gave you? Basically, check for uniqueness before you call save. The code in the second example should be all that you need. – sosborn Jan 17 '12 at 07:44
  • @sosborn The examples only show how the validation itself works (iterate over all previous records looking for the same value), but not where to put it. – Sjors Provoost Jan 17 '12 at 08:42
  • Just make a method that returns a BOOL. If it is unique return YES, else return NO. Call that method. If it return YES, do the save, if it returns no, don't do the save. – sosborn Jan 17 '12 at 08:59
  • @sosborn from where do I call it? From "validateValue:forKey:error:"? – Sjors Provoost Jan 17 '12 at 09:27

3 Answers3

6

Let's say you have a property foo that you want to validate

From Property-Level Validation :

If you want to implement logic in addition to the constraints you provide in the managed object model, you should not override validateValue:forKey:error:. Instead you should implement methods of the form validate<Key>:error:.

Where <Key> is your property. You would actually implement something like:

-(BOOL)validateFoo:(id *)ioValue error:(NSError **)outError {
    return [self isUnique];
}
quellish
  • 21,123
  • 4
  • 76
  • 83
2

validateValue mentioned below will do the validation trick (and correct place to make validation)

If you use NSFetchedResultsController, however, don't forget to remove object from memory to avoid duplicate object in UITableView even on failure. Something like this:

CustomManagedObject *obj = [NSEntityDescription insertNewObjectForEntityForName:@"<YourEntity>" inManagedObjectContext:self.managedObjectContext];
obj.property = @"some property";

if (![self.managedObjectContext save:&error]) {
   [self.managedObjectContext deleteObject:obj]; // delete from memory. Otherwise, you get duplicated value in UITableView even if save has failed
}
atxe
  • 5,029
  • 2
  • 36
  • 50
Shimon
  • 1,434
  • 1
  • 10
  • 9
1

This does the trick, although it is slow on bulk inserts and you still need to create an NSError object.

-(BOOL)validateValue:(__autoreleasing id *)value forKey:(NSString *)key error:(NSError *__autoreleasing *)error {
    [super validateValue:value forKey:key error:error];

    // Validate uniqueness of my_unique_id
    if([key isEqualToString:@"my_unique_id"]) {
        NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
        [fetch setEntity:[NSEntityDescription entityForName:[self.entity name]
               inManagedObjectContext:self.managedObjectContext]];

        NSPredicate *predicate = [NSPredicate 
            predicateWithFormat:@"my_unique_id = %@",[self valueForKey:key]];

        fetch.predicate = predicate;

        NSError *error = nil;
        NSUInteger count = [self.managedObjectContext 
                           countForFetchRequest:fetch error:&error];

        if (count > 1) {
            // Produce error message...

            // Failed validation:
            return NO;
        }


    }

    return YES;
}
Sjors Provoost
  • 1,921
  • 1
  • 19
  • 34
  • 5
    According the Apple's [Managed Object Validation][1], If you want to implement logic in addition to the constraints you provide in the managed object model, you should not override validateValue:forKey:error:. Instead you should implement methods of the form validate:error:. In your case, this is validateMy_unique_id:error: method. [1]: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdValidation.html – Igor Vasilev Apr 02 '15 at 15:50