How to use Key Value Coding to Set Property Values in ABPerson - iphone

Given this key/value pair:
FirstName = "Steve"
I'd like to set the property of an ABPerson in the iPhone Address Book, like so:
ABRecordSetValue(person, kABPersonFirstNameProperty, #"Steve", nil);
The problem is, I won't always know what key is submitted. (It will be equivalent to a property constant without the "kABPerson" before it, and "Property" after it).
The submitted key could be any number of properties: "JobTitle", "Note", etc.
How do I translate that key into the Address Book Property Constant. ie:
FirstName to kABPersonFirstNameProperty.
I was thinking I could use Key Value Coding, but I'm at a loss as to how I'd implement it in this case.
For the sake of coding efficiency, I'm trying to avoid using a long Switch statement, or using an array filled with every possible Property.
Update
I'm discovering here that I need to access a constant by it's string representation. But I don't know if that's possible. So kABPersonJobTitleProperty is a constant that equals 18. So I would love if the following code worked:
NSString *fieldName = [NSString stringWithString:#"JobTitle"];
NSString *propertyName = [NSString stringWithFormat:#"kABPerson%#Property", fieldName];
ABRecordRef person = ABPersonCreate();
ABRecordSetValue(person, propertyName, #"Fisherman", nil);
Of course, running this returns this warning passing argument 2 of 'ABRecordSetValue' makes integer from pointer without a cast. Because the string propertyName is not resolving to the integer or constant it's looking for.
Can anyone see a way to make this work?
My setup is similar to the question listed here:
Key-Value Coding
I'm getting xml data that I have control over, and turning it into properties of an Address Book entry.

This is more of a application design question than anything else. The way I see it with what you have, you're going to need a "long switch statement" somewhere. Maybe the cleanest way would be in a message you send yourself with a signature like:
- (ABPropertyID)recordPropertyFromMyEnum:(MyEnumType)property
Consider this: if the only enumerations you have line up one-for-one with an ABPropertyID, why not just use ABPropertyID instead?

Related

CoreData Object typing won't work

Can someone explain to me why this doesn't work:
CoreDataClass *classObject = (CoreDataClass *)[some method that returns a dictionary with exact KVC pairs that match CoreDataClass];
NSString *myString = classObject.stringProperty;
But this does:
CoreDataClass *classObject = (CoreDataClass *)[some method that returns a dictionary with exact KVC pairs that match CoreDataClass];
NSString *myString = [classObject valueForKey:#"stringProperty"];
EDIT:
What's the easiest way to cast the dictionary as my NSManagedObjectClass CoreDataClass so I can access properties directly?
It doesn't work since KVC compliance is not at all what defines classes or makes them castable - the class hierarchy exists for a reason, and just ensuring adherence to certain methods doesn't magically make something an instance of a completely different class. Keep in mind that the dot-accessor syntax is just sugar for a method send, so these two are equivalent:
classObject.stringProperty
[classObject stringProperty]
...and the latter obviously isn't valid for instances of NSDictionary (i.e. [[NSDictionary class] instancesRespondToSelector:#selector(stringProperty)] is NO).
Your latter example works because of the very premise of your question: if something is KVC-compliant for the key stringProperty, and you ask it for a value for that key, then obviously you get something back. Furthermore, both NSDictionary and CoreDataClass respond to the selector -valueForKey:, so the message send actually works at runtime.
The best way to get the two across isn't a "cast" at all - it's a complete conversion, at the property level, of the data involved. You might consider creating a custom -initWith... method on CoreDataClass that lets you instantiate its properties from a dictionary, or finding a way to get your method to return an actual instance of CoreDataClass instead of an NSDictionary.
Note that this solution may differ from the "easiest" way to get the data across, which is effectively to keep doing what you're doing and use -valueForKey: (though preferably without the cast, which is misleading).
Casting objects only appears to work (in the sense that you won't get type-checking errors) because it's a hint to the compiler, but it doesn't actually change anything about what the pointer points to, so you are still pointing to an NSDictionary. This is because, at the end of the day, you are essentially casting a pointer to a pointer, but telling Xcode that you are allowed to send a different set of selectors to it.
For NSManagedObjects, creation from a dictionary depends on a few things, but the recommended way is to make a class method on your custom class which will use NSEntityDescription and you NSManagedObjectContext, and sets the properties from the dictionary to the object:
+(CoreDataClass *) coreDataObjectWithDictionary:(NSDictionary *) spec {
CoreDataClass *myInstance = [NSEntityDescription insertNewObjectForEntityForName: #"CoreDataClass" inManagedObjectContext: [myMOCProvider sharedMOC];
myInstance.someProp = [spec valueForKey:#"someProp"];
}

dynamic Instance naming

I'm developing an iPad App and need some help.
Through a button within my App I want to create one object at a time.
So every time the button is touched one object should be created.
The problem I have is: I want to assign each object a dynamic name to identify this object.
This would be something like: form0, form1, form2, ..., formN.
This Name corresponds to an instance variable within every object.
So the form1 instance has a number attribute which is 1.
But how do I assign this form1, form2, etc. to a new instance?
I tried to initialize a new instance with the return of a method which creates the formX-String:
-(NSString*)giveMeName{
NSString* simpleName = #"form";
NSString* newName = [simpleName stringByAppendingString:[NSString stringWithFormat:#"%d", questionCounter]];
return newName;
}
where questionCounter is a variable which holds the int identifier for both formX and the instance number attribute.
But when I want to initialize a new instance with this function as name it's not working:
TSForm* [self giveMeName] = [[TSForm alloc] initWithInt:questionCounter headline:headlineText intro:introText];
Obviously I got something wrong with the inner working of Objective-C.
Please help me out.
what you're trying to do isn't really possible. One way that you could achieve the affect you're looking for is using an NSDictionary. For every TSForm object you create, you add that object to the dictionary with the key of the giveMeName return value.
So you start by creating your dictionary:
NSMutableDictionary *formDict = [[NSMutableDictionary alloc] init];
Then, every time you create an object, add it to the dictionary:
id *newTSForm = [[TSForm alloc] init]; // Or however you create a TSForm
[formDict setObject:newTSForm forKey:[newTSForm giveMeName]];
Then when you want to pull out the form you're looking for, you just ask the dictionary based on the name you provided:
[formDict valueForKey:nameOfForm]; // nameOfForm is the name provided by giveMeName
Hope this helps!
use NSMutableArray and keep adding your items there.
Even if what you are trying to do is technically possible, that's using tricsk in low-level objective-C runtime and KVC stuff and so on for nothing.
Using a simple NSMutableArray to keep track of all you instances (and using the index in the array to know which form you are dealing with) is the way to go.
I don't think you really need your unique identifier stuff for that (if so, you are probably thinking about your project the wrong way), as long as you have a way in your code to differentiate each form and manipulate them (the first form created will then be at index 0, the second at index 1… of your NSMutableDictionary)
If you really need this special unique identifier anyway for some strange reason, you can still use an NSMutableDictionary and use the unique identifier as your key of your dict and the form as the associated value. But you should probably think twice about your architecture ad the real need for this before, as it seems quite strange app architecture/design to do so based on your description of your needs in your question.
What you are looking for is some kind of variable variable, which don't really exist in objective-C.
This question (Objective C Equivalent of PHP's “Variable Variables”) has some different suggestions for getting similar results.

Extracting the copyable identity of the non-copyable object

I need to use non-copyable objects as keys in the NSMutableDictionary which, by default, is not allowed. I understand the reasons for this not being allowed (retaining the key object, unlike the value object, is undesirable), but it seems like in my particular situation there could be a way around this.
The thing is that I'm only need to query the dictionary using the key's address, i.e. having the lookup predicate
if (providedKey == storedKey)
instead of
if ([providedKey isEqual:storedKey])
would be perfectly sufficient.
Is there a way of extracting the object's reference address (or other form of identity) as a copyable comparable object which I could use as a dictionary key instead of the object itself?
It seems that +[NSValue valueWithPointer:] might be what you want. It stores the pointer itself in an object that conforms to NSCopying, so that you can use it as a dictionary key. Retrieve the pointer using pointerValue.
You could use [NSString stringWithFormat:#"%p", someObject] as your key.
This will create a string with the object's address as a hex value.
You could use an nsnumber that represents the hash of the object.
you can drop down to the CoreFoundation APIs and define you own callbacks (among other things).
NSMutableDictionary is a CFMutableDictionary.
specifically, you want to create a CFMutableDictionary, and define your own CFDictionaryKeyCallBacks.

Determine whether NSManagedObject attribute is optional

I am using Core Data and have a subclass of NSManagedObject called Person (which is an entity in Core Data). The Person object has several properties (e.g. firstName, lastName, etc.) and relationships (e.g. friends, coWorkers). Some of these attributes are optional, whereas others are mandatory.
Given a Person object how can I efficiently determine whether a particular property or relationship is optional or mandatory?
I want to try and avoid having to do some kind of inefficient loop through each attribute of the Person object, as I will need to call the code fairly often, so something similar to the pseudocode below would be perfect:
if ( [[aPerson getProperty:#"firstName"] isOptional] ) {
// do stuff
}
...but I am not sure whether it could be done that simply. The above pseudocode would also only check properties, so I am guessing something else would need to be done in case the attribute being checked is a relationship.
I realize that it is probably more efficient to do a single loop and store the name of all the mandatory attributes in an array (then just check that array for the presence of the attribute name) and that's what I am doing at the moment, but unfortunately it doesn't really work with the structure of my code (thus the need for a more "on-demand" approach).
Any help would be greatly appreciated :)
You can write a function in your base subclass
NSDictionary *d = [[self entity] attributesByName];
NSAttributeDescription *attr = (NSAttributeDescription *)[d objectForKey:#"test"];
BOOL isopt = [attr isOptional];

Dealing with <'null>' values in TouchJSON

I am deserializing some JSON using the TouchJSON framework. The array of dictionaries that comes from the parsing is used to populate a UITableView.
There is a chance that some of the values in the JSON I parse are empty. This results, if I NSLog it to the console, in the values looking like this:
id = 1234;
title = "Hello, World";
description = "<null>";
detail = "The world says hello";
Here the description value was an empty string when retrieved from the server.
So TouchJSON recognizes that the description values is of type string, but the original intention of the server was to communicate that this was an empty string, like description = #"";
If I later on try to set the value of description, to a UILabels text property the app will crash.
So my questions are, I have both NSNumbers and NSStrings in the JSON, should I traverse the result from TouchJSON's deserialize method and test all values and how would I do so?
I can't simulated what would happen if an NSNumber value was empty, how would I test for this? Will the NSNumber value be nil in that case instead of "null"?
I was using the SBJSON library and came up against the same problem. My solution would apply to your case too: I changed the library so that it handled missing values, setting them to +[NSNull null] in the collection it returned. That makes your client code a little warty, because you have to handle the cases where you might get an NSNull instead of an NSString. But this is just a more obvious version of the wart where you have to decide whether #"" meant an empty string or an unset value.