Sjors Provoost
Sjors Provoost

Reputation: 1931

How do I make a custom validation (for uniqueness) in Core Data?

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.

Upvotes: 8

Views: 3746

Answers (3)

Shimon
Shimon

Reputation: 1464

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
}

Upvotes: 2

quellish
quellish

Reputation: 21254

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];
}

Upvotes: 6

Sjors Provoost
Sjors Provoost

Reputation: 1931

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;
}

Upvotes: 1

Related Questions