Search code examples
validationcore-datarestkitkvc

RestKit KVC Validation won't call validation methods


this is my first question here :)

OK so I have a projet with ReskKit 0.23.3 via cocoapods. I use RestKit/CoreData.

I fetch an URL, the result got mapped to my object and is correctly saved by core data. I want to use Key-Value Validation to check some values retrieved against the one already persisted. I read that i could use the methods validateKey:error: on my NSManagedObject. Somehow, it is never called. I'm frustrated...

Here are my files (for simplicity, i concatenated logic code into one flat file here):

JSON response /collections/{id}

{
    "id": "00000000-0000-0000-0000-00000000000",
    "image_url": "http://server/image.png",
    "name": "Collection C",
    "etag": 42,
    "ctag": 42
}

Collection.h

@interface Collection : NSManagedObject

@property(nonatomic, strong) NSString *collectionId;
@property(nonatomic, strong) NSString *name;
@property(nonatomic, strong) NSURL *imageUrl;
@property(nonatomic, strong) NSNumber *etag;
@property(nonatomic, strong) NSNumber *ctag;

@end

Collection.m

@implementation Collection

@dynamic collectionId, name, imageUrl, etag, ctag;

- (BOOL)validateCollectionId:(id *)ioValue error:(NSError **)outError {
    NSLog(@"Validating id");
    NSLog(@"Coredata collection id: %@", self.collectionId);
    NSLog(@"GET collection id: %@", (NSString *)*ioValue);
    return YES;
}

- (BOOL)validateEtag:(id *)ioValue error:(NSError **)outError {
    NSLog(@"Validating etag");
    NSLog(@"Coredata collection etag: %@", self.etag);
    NSLog(@"GET collection etag: %@", (NSString *)*ioValue);
    return YES;
}

@end

Code

NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];

[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingString:@"/MyApp.sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);

[managedObjectStore createManagedObjectContexts];

managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];

[RKManagedObjectStore setDefaultStore:managedObjectStore];

NSURL *url = [NSURL URLWithString:@"http://server/api"];

RKObjectManager *objectManager  = [self managerWithBaseURL:url];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
objectManager.managedObjectStore = [RKManagedObjectStore defaultStore];

RKEntityMapping *collectionMapping = [RKEntityMapping mappingForEntityForName:@"Collection" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[collectionMapping addAttributeMappingsFromDictionary:@{@"id": @"collectionId",
                                                        @"image_url": @"imageUrl"}];
[collectionMapping addAttributeMappingsFromArray:@[@"name", @"etag", @"ctag"]];
[collectionMapping setIdentificationAttributes:@[@"collectionId"]];

RKResponseDescriptor *collectionResponseDescriptors = [RKResponseDescriptor responseDescriptorWithMapping:collectionMapping
                                                                                                   method:RKRequestMethodGET pathPattern:@"collections/:collectionId"
                                                                                                  keyPath:nil
                                                                                              statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:collectionResponseDescriptor];

[objectManager getObjectsAtPath:@"collections/00000000-0000-0000-0000-00000000000" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
    Collection *collection = (Collection *)[mappingResult.array firstObject];
    NSLog(@"Collection: %@", collection);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
    NSLog(@"Oh noes :(");
}];

Output

2014-09-13 12:39:04.242 MyApp[41958:607] I restkit:RKLog.m:33 RestKit logging initialized...
2014-09-13 12:39:05.028 MyApp[41890:607] Collection: <NSManagedObject: 0x9108a60> (entity: Collection; id: 0x94166d0 <x-coredata://6645F428-7631-45F0-A8AF-E2352C50F35E/Collection/p1> ; data: {
    collectionId = "00000000-0000-0000-0000-00000000000";
    ctag = 42;
    etag = 42;
    imageUrl = "http://server/image.png";
    name = "Collection C";
})

So, I get my Log with the Collection, but None of the NSLog in the validate<Key>:error: methods got triggered... Could not figure out why!

Edit

With some breaks, i figured this is RKMappingOperation who is responsible for calling those validation methods on my object. Precisely it's validateValue:atKeyPath:

RKMappingOperation.m

...
- (BOOL)validateValue:(id *)value atKeyPath:(NSString *)keyPath
{
    BOOL success = YES;

    if (self.objectMapping.performsKeyValueValidation && [self.destinationObject respondsToSelector:@selector(validateValue:forKeyPath:error:)]) {
        NSError *validationError;
        success = [self.destinationObject validateValue:value forKeyPath:keyPath error:&validationError];
        ...
    }
...

But self.destinationObject is an NSManagedObject and not a Collection object...

Console

(lldb) po [self.destinationObject class]
NSManagedObject

I hope you could lead me to the right way :) Thank you!


Solution

  • It appears that you have not specified that the entity should use the Collection class in the Core Data model. If you don't specify anything then NSManagedObject will be used by default.

    Demo image from the web