Relation to property names and data? - objective-c

In many uses of data one thing confuses me. EG in CoreData and the NSManagedObject subclass's property names are the same as the properties in the CoreData attributes. Similar things go on with the NSXMLParser.
Why is this? it's confusing to me because I never see the string for the name of attribute being used. While NSLoging a fetch request object I get,
"<Person: 0x6d5be20> (entity: Person; id: 0x6d5dce0 <x-coredata://6800B6A5-87AF-46B4-9836-9D412A9B3EE4/Person/p1> ; data: {\n age = 51;\n firstName = Anthony;\n lastName = Robbins;\n})",
As one of the array elements. I wouldn't expect any different.
But in the tutorial when I itterate through the array using
for (Person *thisPerson in persons)
(thisPerson being the data object with the mysterious properties). The property names seem to have been sorted according to name.
NSLog(#"First Name = %#", thisPerson.firstName);
Why the hell is this? Surely a barrage of NSRegex expressions and string modifications would be required to extrapolate this data.

As for Core Data (not NSXMLParser) Core Data simply uses the property name to look up the corresponding property in the model. If you're accessing lastName on an NSManagedObject instance with foo.lastName there should be no implementation for
-(NSString *)lastName
This will cause the NSObject class to fall back to NSKeyValueCoding which will turn that call into
[foo valueForKey:#"lastName"]
The NSManagedObject class overrides -valueForKey: (and other NSKeyValueCoding relating methods) and uses that to access data through the NSPersistentStoreCoordinator.

Related

Objective-C - aligning class properties with json dictionary data

I'm relatively new to Objective-C (mainly using cocoa/apple foundation framework), but a long-time C++ programmer, so I'll start by explaining what I'm trying to accomplish; It's quite possible my approach isn't the best one, so I'd love to hear any suggestions for a different approach.
I've got a text file in json format, just made this quick example:
"section1" : {
"director" : "Sample Name 1",
"writers" : {
"name" : "Example Name 1",
"name" : "Example Name 2",
},
},
And I've got a class with properties which I want to match with the data inside this file, since I'm planning to parse this file and store some of the values in an instance of this class. My class header would look roughly like this:
#interface SongData : NSObject
#property (nonatomic, copy) NSString *director;
#property (nonatomic, copy) NSArray *writers;
#end
So I'm trying to find the cleanest, somewhat "data-driven" way to store this json data into the correct properties. Basically I want a way to loop through the json "dictionary" of data, and somehow use the "keys" to match with the correct property in my class and assign the value to that property. My first crack at it, I created a dictionary where the "key" is the actual key to lookup the proper data inside the json data, and the "value" would be a pointer to the correct class property. Sort of like this:
NSDictionary *descriptionDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
director, #"name",
writers, #"writers",
nil];
Then the idea was to loop through the json data (which is stored in another NSDictionary), use each key from my "descriptionDictionary" to lookup the appropriate value in the json data, then use the matching "value" of the description dictionary based on the same key (which I was hoping is sort of a pointer to the actual class property?) and set that properties value to the value from the json data. Perhaps an example will make it a bit more clear :-). Lets assume "jsonDictionary" is the result of parsing my json data, and I'm already inside section1, so there should be a 1-1 match between keys that make up the jsonData NSDictionary and the keys that make up my descriptionDictionary NSDictionary:
[descriptionDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
value = [jsonDictionary objectForKey:propertyName];
}];
Now "value" would technically be pointing to one of my classes properties, and I want to assign relevant json data to it (ie director = #"Sample Name 1"). I know there's problems with what I'm doing (I realize that I probably can't just dereference a pointer to a random class property and assign an arbitrary object of unknown type to it, hoping it all matches up :-) but hoping my example at least illustrates what I'm trying to do so someone can tell me a better way.
Have a look at KVC (source: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/BasicPrinciples.html)
Setting Attribute Values Using Key-Value Coding
The method setValue:forKey: sets the value of the specified key,
relative to the receiver, to the provided value. The default
implementation of setValue:forKey: automatically unwraps NSValue
objects that represent scalars and structs and assigns them to the
property. See “Scalar and Structure Support” for details on the
wrapping and unwrapping semantics.
If the specified key does not exist, the receiver is sent a
setValue:forUndefinedKey: message. The default implementation of
setValue:forUndefinedKey: raises an NSUndefinedKeyException; however,
subclasses can override this method to handle the request in a custom
manner.
The method setValue:forKeyPath: behaves in a similar fashion, but it
is able to handle a key path as well as a single key.
Finally, setValuesForKeysWithDictionary: sets the properties of the
receiver with the values in the specified dictionary, using the
dictionary keys to identify the properties. The default implementation
invokes setValue:forKey: for each key-value pair, substituting nil for
NSNull objects as required.
One additional issue that you should consider is what happens when an
attempt is made to set a non-object property to a nil value. In this
case, the receiver sends itself a setNilValueForKey: message. The
default implementation of setNilValueForKey: raises an
NSInvalidArgumentException. Your application can override this method
to substitute a default value or a marker value, and then invoke
setValue:forKey: with the new value.
As long as your class properties have the same names as the JSON fields you can use the setValuesForKeysWithDictionary: and pass in the JSON root dictionary.
For keys/properties that are named differently you can simply override the setValue:forUndefinedKey: and set the appropriate property yourself.
Finally there is the case of a value being represented by a different type in the JSON than in the property. For example NSURL would be an NSString instead. Here you can simply check the class of the passed parameter and if it does not match the IVAR, do a conversion.

Reading from Core Data

I want to read some data from Core Data for statistics. My datamodel is like in the tutorials found in the internet (a bit of clicking and voilà it is ready). But now I want to work with the data.
My function:
-(int)calcAve {
int ret=0;
ret = [[stats valueForKey:#"aveScore"] intValue];
NSLog(#"%d",ret);
return ret;
}
stats is the object connected from the .xib to the class, which. This object is bound to the entity Stats in the datamodel. [stats entity] returns the correct value.
aveScore is one object in the entity stats (no misspelling, checked it multiple times!). No the error shows me, that stats is not the correct datamodel:
this class is not key value coding-compliant for the key
What is wrong with this? Is there a simple way to read out the data from Core Data?
I am not sure what you managedObject in this case is. However usually you want to store more than one managedObject of the same class in a managedObjectContext. For example several recipes objects in a MOC. Every recipe has the properties.
Your call for value for key sounds reasonable. Check what that class of the stats object is. If it is not of the class NSManagedObject or your custom subclass of it you have a problem. You can only store NSManagedObjects within CoreData. You can check it like this:
NSString *className = NSStringFromClass([stats class]);
NSLog(#"class name: %#",className);
The way you are asking I suggest you try to read the CoreData docs again and look at the Recipes example in Apple's sample code library.

CoreData Optional Relationships and JSON null

I have a CoreData model with 2 objects. The first object (Images) contains a list of image names. The second object (Book) has a relationship with a field from the Images object called imageId. The data type is set to int16 and is marked optional with a default value of 0, as not every book will have an image.
I request a JSON representation of the Book object from a WCF service. One of the book objects returned has "ImageId" : null as its value. So when converting the JSON object into a managed object in Objective-C I get the following error message:
Unacceptable type of value for attribute: property = "imageId"; desired type = NSNumber; given type = NSNull; value =
How do you handle this conversion? I thought of checking for null and then setting the value to 0 but this doesn't seem correct.
Many thanks.
*UPDATE *
In the process of implementing Daniels solution I discovered that other fields could also be returned from the service as null. Is it therefore possible to modify the NSDictionary category to enable it to traverse itself and replace any instance of with nil? I am using jsonKit as my JSON parser and it seems to return the parsed result as a NSMutableDictionary with NSDictionary objects representing my JSON objects. I have spent much of the day trying to modify my objects but as they are returned as NSDictionaries they are immutable. I could simply use Daniels solution on every field that needed checking for however if the service changed and another field was returned as then this would break my app. Alternatively I could implement Daniels solution on every field just incase it was ever but not sure if thats a good solution especially for performance.
Some JSON libraries let you set an option so that JSON nulls are simply omitted from the dictionary as opposed to using NSNull. If you do that, then it will work because [theDict objectForKey:#"imageID"] will return nil.
But other than that, the right thing to do is look out for NSNull explicitly. Making an NSDictionary category is a handy way to do it:
#implementation NSDictionary (DDNullGetter)
- (id)nonNullObjectForKey:(id)key
{
id val = [self objectForKey:key];
return (val == [NSNull null] ? nil : val);
}
#end

How to test property existence and type based on NSString typed key?

In my quest to update a Core Data model within my iOS project, I'm querying a server for JSON objects that correspond - to some extent - with the managed entities of my model. The end result I'm striving for is a reliable update solution from JSON output.
For the examples in this question, I'll name the core data managed object existingObj and the incoming JSON deserialized dictionary updateDict. The tricky part is dealing with these facts:
Not all properties of the existingObj are present in the updateDict
Not all properties of the updateDict are available in the extistingObj.
Not all types of existingObj's properties match the JSON deserialized properties. (some strings may need a custom Objective-C wrapper).
updateDict may contain values for keys that are uninitialized (nil) in existingObj.
This means that while iterating through the updated dictionaries, there has to be some testing of properties back and forth. First I have to test whether the properties of the updateDict exist in existingObj, then I set the value using KVC, like so:
// key is an NSString, e.g. #"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
[existingObj setValue:[updateDict objectForKey:key] forKey:key];
}
Although this part works, I don't like the fact that I'm actually testing for displayName as a getter, while I'm about to call the setDisplayName: setter (indirectly via KVC). What I'd rather to is something like [existingObj hasWritablePropertyWithName:key], but something that does this I can't find.
This makes for subquestion A: How does one test for a property setter, if you only have the property's name?
The next part is where I'd like to automate the property identification based on their types. If both the updateDict and the existingObj have an NSString for key #"displayName", setting the new value is easy. However, if the updateDict contains an NSString for key #"color" that is #"niceShadeOfGreen", I'd like to transform this into the right UIColor instance. But how do I test the type of the receiving property in existingObj so I know when to convert values and when to simply assign? I was hoping for something along the lines of typeOfSelector:
if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
// regular assignment
} else {
// perform custom assignment
}
Of course this is boguscode. I can't rely on testing the type of the existingObj-property's value, for it may be unitialized or nil.
Subquestion B: How does one test for the type of a property, if you only have the property's name?
I guess that's it. I figured this must be a dupe of something that's already on here, but I couldn't find it. Maybe you guys can?
Cheers, EP.
P.S. If you'd have a better way to synchronize custom Objective-C objects to deserialized JSON objects, please do share! In the end, the result is what counts.
If you want to query whether an object has a setter for a given KVC key called key which corresponds to a declared property, you need to check whether it responds to a selector method called setKey: (starts with set, capitalise the first character in key, add a trailing colon). For instance,
NSString *key = #"displayName";
NSString *setterStr = [NSString stringWithFormat:#"set%#%#:",
[[key substringToIndex:1] capitalizedString],
[key substringFromIndex:1]];
if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
NSLog(#"found the setter!");
[obj setValue:someValue forKey:key];
}
Two remarks:
Even though properties can have setters with names that do not follow the pattern described above, they wouldn’t be KVC compliant, so it is safe to check for set<Key>: since you’re using KVC to set the corresponding value.
KVC doesn’t use the setter method only. If it doesn’t find a setter method, it checks whether the class allows direct access to instance variables and, if so, use the instance variable to set the value. Also, if no setter method or instance variable is found, it sends -setValue:forUndefinedKey: to the receiver, whose class might have overridden the standard implementation that throws an exception. This is described in the Key-Value Coding Programming Guide.That said, if you’re always using properties, checking for the setter method should be safe.
As for your second question, it is not possible to query the runtime to know the actual Objective-C class of a property. From the runtime perspective, there’s an implementation specific type encoding for properties and general types (such as method parameters/return types). This type encoding uses a single encoding (namely #) for any Objective-C object, so the type encoding of an NSString property is the same as the type encoding of a UIColor property since they’re both Objective-C classes.
If you do need this functionality, one alternative is to process your classes and add a class method that returns a dictionary with keys and corresponding types for every property (or the ones you’re interested in) declared in that class and superclasses, or maybe some sort of description language. You’d have to do this on your own and rely on information not available during runtime.

What is the difference between valueforKey:, objectForKey:, and valueForKeyPath:?

I have 2 questions:
What is the difference between valueForKey: and objectForKey:? Is it that one is for NSDictionarys (objectForKey:) and for others it is valueforKey:, or is it the reverse?
Also what is the difference between valueForKey: and valueForKeyPath:? Has it got something to do with Core Data?
Please help.
valueForKey: is part of the NSKeyValueCoding protocol and is therefore part of the key-value coding framework, which allows you to access class properties by name at runtime. That's how NIBs are loaded, for example — the names of properties associated with connections are loaded and then the values are set directly by name. This contrasts with the way that visual interface design tools often work in other languages, generating lots of hidden statically compiled code.
objectForKey: is defined on dictionaries only, and looks up an object by its key. An NSDictionary is a class that stores connections between values and keys.
So, valueForKey: could be used on an NSDictionary to return meta information about the dictionary, such as the count of objects inside it, the list of all keys, etc. objectForKey: would be used actually to look into the dictionary.
At runtime, the difference is that objectForKey: is a method with a completely opaque implementation. valueForKey: explicitly relies on subsequently calling named getters and setters. The reason for the latter is that you can extend key-value coding to key-value observing, where you ask to be informed every time a particular property on a particular object changes. At runtime that's achieved with a method swizzle, where the original setter is replaced by a new one that calls the previous setter and then sends out the required messages. Because all messages are dispatched dynamically, that's just achieved by modifying tables within the object instance.
So any object that is key-value coding compliant (which just means declaring and implementing your properties in the proper way, which the new-ish #property/#synthesize syntax does automatically) can be observed without the object itself having to implement any code.
There's further Apple stuff that uses key-value coding to achieve various things, including CoreData, but it's not specifically to enable any one other technology.
valueForKeyPath: is like valueForKey: except that it can traverse several objects. So you can have a root object with a bunch of properties, each of those properties is another object with another bunch of properties, etc, and using a key path you can retrieve a value way out at the leaf of that data structure rather than having to iterate through object after object for yourself.
In summary, valueForKey: and valueForKeyPath: provide information about object instances and interact with the dynamic nature of the Objective-C runtime. objectForKey: is a dictionary specific method that does dictionary tasks.
Additions:
An example, coded as I type and assuming that NSDictionary is key-value coding compliant:
NSDictionary *someDictionary;
// create someDictionary, populate it, for example (note: we assume photoOfKeys.jpg
// definitely exists, not a good idea for production code — if it doesn't we'll get
// a nil there and anything after it won't be added to the dictionary as it'll appear
// that we terminated the list):
someDictionary = #{ #"favouriteGarment": #"hat",
#"#allKeys" : [NSImage imageNamed:NSImageNameDotMac],
#(2) : NSArray.new };
NSObject *allKeys;
// we make no assumptions about which type #allKeys will be, but are going to assume
// we can NSLog it, so it needs to be a descendant of NSObject rather than 'id' so as
// to definitely respond to the 'description' message — actually this is just compile
// time semantics, but if someone else reads this code it'll make it obvious to them
// what we were thinking...
// some code to get all of the keys stored in the dictionary and print them out;
// should print an array containing the strings 'favouriteGarment', '#allKeys' and
// the number 2
allKeys = [someDictionary valueForKey:#"#allKeys"];
NSLog(#"%#", allKeys);
// some code to get the object named '#allKeys' from the dictionary; will print
// a description of the image created by loading photoOfKeys.jpg, above
allKeys = [someDictionary objectForKey:#"#allKeys"];
NSLog(#"%#", allKeys);
// `objectForKey is analogous to `objectForKeyedSubscript:`, aka
allKeys = someDictionary[#"#allKeys"];
allKeys is a property of NSDictionary as described here. I've also added a mapping from the NSString allKeys to a photograph of some keys. Whether I use the key-value coding valueForKey: methods or the NSDictionary objectForKey: lookup method dictates whether I read the property of the object instance or whether I send the object instance a message asking it to do its unique job.
objectForKey: is a method on NSDictionary for accessing the object associated with a key. valueForKey: is a method on NSObject for accessing any value associated with any object, through the name of a accessor method, property, and/or instance variable.
valueForKeyPath: can be seen as a shorthand for several calls to valueForKey:. You can think of it as sort of a xpath, if you will.
These two statements will result in the same output:
// Using nested valueForKey:
NSLog(#"%#", [[myObject valueForKey:#"foo"] valueForKey:#"bar"]);
// Can be done with a single valueForKeyPath;
NSLog(#"%#", [myObject valueForKeyPath:#"foo.bar"]);
valueForKey:and valueForKeyPath: are part of KVC (Key Value Coding). Introduction and in-depth documentation can be found here: http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/
valueForKey: and valueAtKeyPath: are methods defined in the NSKeyValueCoding informal protocol, and default implementations for both are provided by the root class NSObject.
objectForKey: is a method on NSDictionary.
valueForKey: takes a key to a property, while valueAtKeyPath: takes a so-called keypath. A keypath is a period-delimeted path to a specific property, like #"relationship.property".