Reputation: 1013
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
Upvotes: 2
Views: 2828
Reputation: 629
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.
Upvotes: 3