Hello everyone. Many discussions address this in many development forums. I 
read them all, and was frustrated NOTHING of the suggested solutions actually 
worked. Apple's documentation adds insult to injury, completely ignoring such a 
common need for any real-world  Core-Data modeled application.

I have an entity "Species" with a numerical attribute "catalogID" which has 
"human" meaning (every digit will tell something about the species, plus some 
free digits). Different species entities must have unique catalog IDs much like 
social security number for people, or ISBN for books. I have no more than few 
hundred "Species" entities, and they have many attributes and relations.

My wish is simple. To emit an error dialog when a user tries to use an existing 
ID when creating a new species in the UI (source/detail window), or when trying 
to edit the ID of an existing species, changing it to a value taken for another 
species.

After much reading, I implemented KVC validation (validate<key>:error:) in my 
NSManagedObject subclass "PMSpecies" like thus:

-(BOOL)validateCatalogID:(id *)ioValue error:(NSError * __autoreleasing 
*)outError {
    // Prepare optimized static request to only fetch all species catalogID's 
for reuse every time we validate the edited species catalogID.
    static NSFetchRequest *allSpeciesIDsFetchRequest = nil;
    if (allSpeciesIDsFetchRequest == nil) {
        allSpeciesIDsFetchRequest = [NSFetchRequest 
fetchRequestWithEntityName:[[self entity] name]];
        [allSpeciesIDsFetchRequest setPropertiesToFetch:@[ @"catalogID" ]];
        [allSpeciesIDsFetchRequest setResultType: NSDictionaryResultType];
    }
    NSArray *allSpecies = [self.managedObjectContext 
executeFetchRequest:allSpeciesIDsFetchRequest error:nil];
    NSArray *allCatalogIDs = [allSpecies valueForKeyPath:@"catalogID"];
    if ([allCatalogIDs containsObject:*ioValue]) {
        if (outError != NULL) {
            NSDictionary *userInfoDict = @{ NSLocalizedDescriptionKey : 
[NSString stringWithFormat:NSLocalizedString(@"The ID %@ is already taken by 
another species. Please use a unique ID.", NULL), *ioValue] };
            *outError = [[NSError alloc] initWithDomain:@"Model Validation" 
code:eExistingCatalogID userInfo:userInfoDict];
        }
        return NO;
    }
    return YES; // valid ID
}


I was happy enough to see that the bound NSTextField in the UI now behaved 
correctly, and emitted error upon adding/updating a species with a non-unique 
ID. However - the moment I try to save my context  to the persistent store, I 
receive hundreds of "uniqueness" errors for every species. 

I saw that the above validation method was called for every species on every 
save, and in those calls, my "fetch" indeed finds one occurrence of the  ID --- 
the actual NSManagedObject's! which is OK.

So this kind of validation can't work because it is used in 2 different 
situations - BEFORE the value was set to the attribute, and AFTER is was set 
(upon saving the context). But in this method, I don't have a "context" 
meaning, I don't know what kind of validation is this.
 

I tried to implement instead the validateForInsert: and validateForUpate: 
methods, but then the UI remains stupid, and only when the user actually saves 
the document (long after closing the "Species" window) he receives strange 
validation errors. In these implementations, I detect violations by finding 2 
or more of the same ID - not just one as the above.

Apple CoreData Programming Guide tells you the following:

NSManagedObject provides consistent hooks for validating property and 
inter-property values. You typically should not override 
validateValue:forKey:error:, instead you should implement methods of the form 
validate<Key>:error:, as defined by the NSKeyValueCoding protocol. If you want 
to validate inter-property values, you can override validateForUpdate: and/or 
related validation methods. 

But there is absolutely nothing about inter-entity validation, such as value 
uniqueness, value ordering dependency upon previous entities like serial IDs or 
any cross-entity validation. 

Can anyone shed light on the subject? 

At my current situation, I would gladly settle on a UI-only validation 
(meaning, something that would validate a text field upon end-edit-session, but 
I don't know how to hook this on a bound-to-model field either. 

Please help...

Motti Shneor
---
It is impossible to make anything foolproof, because fools are so ingenious.  
        - Mark Twain

Make something fool-proof, and only fools will use it.
         - My own addition

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to