views:

231

answers:

2

I need a little help about KVC.

Few words about the operating context:

1) The iPhone connects (client) to a webService to get an object,

2) I'm using JSON to transfer data,

3) If the client has exactly the same object mapping I can iterate through the NSDictionary from JSON to store the data in the permanent store (coreData). To do that I'm using this code fragment (assume that are all data are NSString):

NSDictionary *dict = ... dictionary from JSON

NSArray *keyArray = [dict allKeys]; //gets all the properties keys form server

for (NSString *s in keyArray){

[myCoreDataObject setValue:[dict objectForKey:s] forKey:s];  //store the property in the coreData object 

}

Now my problem ....

4) What happen if the server implementing a new version of the object with 1 new property If I transfer the data to the client and the client is not at the save version level (it means is still using the "old" object mapping) I'll try to assign a value for a non existing key... and I will have the message:

the entity "myOldObject" is not key value coding-compliant for the key "myNewKey"

Could you suggest me how to test for the existence of the key in the object so, if the key exists, I'll can proceed to the value update avoiding the error message ?

Sorry if I have been a little confusing in my context explanation.

Thanks

Dario

+1  A: 

While I can't think of a way to find out what keys will an object support you could use the fact that when you set the value for a nonexistent key the default behaviour of your object is to throw an exception. You could enclose the setValue:forKey: method invocation in a @try / @catch block to handle these errors.

Consider the following code for an object:

@interface KVCClass : NSObject {
    NSString *stuff;
}

@property (nonatomic, retain) NSString *stuff;

@end

@implementation KVCClass

@synthesize stuff;

- (void) dealloc
{
    [stuff release], stuff = nil;

    [super dealloc];
}

@end

This should be KVC-compliant for the key stuff but nothing else.

If you access this class from the following program:

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    KVCClass *testClass = [[KVCClass alloc] init];

    [testClass setValue:@"this is the value" forKey:@"stuff"];

    NSLog(@"%@", testClass.stuff);

    // Error handled nicely
    @try {
        [testClass setValue:@"this should fail but we will catch the exception" forKey:@"nonexistentKey"];
    }
    @catch (NSException * e) {
        NSLog(@"handle error here");
    }

    // This will throw an exception
    [testClass setValue:@"this will fail" forKey:@"nonexistentKey"];

    [testClass release];
    [pool drain];
    return 0;
}

You will get a console output similar to the following:

2010-01-08 18:06:57.981 KVCTest[42960:903] this is the value
2010-01-08 18:06:57.984 KVCTest[42960:903] handle error here
2010-01-08 18:06:57.984 KVCTest[42960:903] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<KVCClass 0x10010c680> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key nonexistentKey.'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x00007fff851dc444 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x00007fff866fa0f3 objc_exception_throw + 45
    2   CoreFoundation                      0x00007fff85233a19 -[NSException raise] + 9
    3   Foundation                          0x00007fff85659429 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 434
    4   KVCTest                             0x0000000100001b78 main + 328
    5   KVCTest                             0x0000000100001a28 start + 52
    6   ???                                 0x0000000000000001 0x0 + 1
)
terminate called after throwing an instance of 'NSException'
Abort trap

This shows that the first attempt to access the key nonexistentKey was nicely caught by the program, the second one generated an exception similar to yours.

szzsolt