3

I am trying to present a way for the user to enter addresses in a location based application, and I want it to look exactly like the address from the iPhone Contacts/Address Book. This means that I need the placeholder text for each field to be updated according to the country selected.

For example-

United States placeholders are: Street, City, State, ZIP

United Kingdom placeholders are: Street, City, County, Post Code

I also have to make sure that the placeholders map to the correct field in code (i.e. that "County" maps to state and that "Post Code" maps to zip). My current approach is very cumbersome: go through and select each country in a contact and copy the text by hand, then make sure the fields map correctly.

Is there ANY programmatic way to do this? Even if it is through a private API call, I only need to do it once to get the info, then I can remove the code. I can't find anything in Apple's documentation to provide this type of information (ABAddressBook documentation).

Matt
  • 22,721
  • 17
  • 71
  • 112
thephatp
  • 1,479
  • 1
  • 20
  • 37

1 Answers1

1

Not sure, if I get you right. You need the locale name of each field - e.g. the locale translation, in french, german, etc?

In general there are not to much fields for addresses - see: ABPerson.h header file:

// Addresses
extern const ABPropertyID kABPersonAddressProperty;            // Street address - kABMultiDictionaryPropertyType
extern const CFStringRef kABPersonAddressStreetKey;
extern const CFStringRef kABPersonAddressCityKey;
extern const CFStringRef kABPersonAddressStateKey;
extern const CFStringRef kABPersonAddressZIPKey;
extern const CFStringRef kABPersonAddressCountryKey;
extern const CFStringRef kABPersonAddressCountryCodeKey;

And if you need the field name (cause there could be personal fantasy names ;) ), be sure to use ABAddressBookCopyLocalizedLabel like this:

CFStringRef label = ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(multiValue, j));

Maybe you like to clarify your question, if I got you wrong.

jimmy

EDIT:

Ok, I'm still not sure if I got you right, but I will answer 'both' ways I got you ;)

The first is that you want the generic field names (locale independent) - you can get those (in the current locale of you project) that way: The code is using NSArrays and NSDictionaries depending of the entry of the address book card:

- (void) logAddressBook {

    ABAddressBookRef addressBook = ABAddressBookCreate();

    NSArray *addresses = (NSArray *) ABAddressBookCopyArrayOfAllPeople(addressBook);

    int i;
    for(i = 0; i < [addresses count]; i++) {
        ABRecordRef record = [addresses objectAtIndex:i];
        NSString *firstName = (NSString *)ABRecordCopyValue(record, kABPersonFirstNameProperty);
        NSString *lastName = (NSString *)ABRecordCopyValue(record, kABPersonLastNameProperty);

        NSLog(@"%@, %@", lastName, firstName);

        ABMultiValueRef multiValue = ABRecordCopyValue(record, kABPersonEmailProperty);

        int count = ABMultiValueGetCount(multiValue);
        int j;
        for(j = 0; j < count; j++) {
            CFStringRef label = ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(multiValue, j));
            NSString *value = (NSString *)ABMultiValueCopyValueAtIndex(multiValue, j);

            NSLog(@"Email for %@: %@", label, value);

            CFRelease(label);
        }

        //Get the contact´s addresses
        CFTypeRef adressesReference = ABRecordCopyValue((ABRecordRef)record, kABPersonAddressProperty);    

        CFIndex mvCount = ABMultiValueGetCount(adressesReference);
        if (mvCount > 0) {
            NSLog(@"Addresses: ");    
            for (j=0; j < mvCount; j++) {
                CFStringRef key = ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(adressesReference, j));
                NSDictionary *values = (NSDictionary *)ABMultiValueCopyValueAtIndex(adressesReference, j);

                NSLog(@"%@ - ", key);
                NSEnumerator *enumerator = [values keyEnumerator];
                id innerKey;

                while ((innerKey = [enumerator nextObject])) {
                    /* code that uses the returned key */
                    NSString *value = (NSString *)[values objectForKey: innerKey];
                    CFStringRef innerKeyLabel = ABAddressBookCopyLocalizedLabel((CFStringRef)innerKey);
                    NSLog(@"key: %@ -> value: %@", innerKeyLabel, value);
                }    

            }             
        }

        CFRelease(adressesReference);

    }

}

Look at the log and you see how to get all labels and values you like - just extend the code to the fields you want.

The other part of my answer: I wonder if you just wanted to see the labels in the different languages a user might have as locale. Such as french, german, etc. If you want to see this (having ABAddressBookCopyLocalizedLabel to use different languages) I only found 'Localization native development region' in the .plist file of the project. If you change this, the translation is changed. Showing you the labels in the users language.

I'm not sure if it is possible to change this programmatically. If you know a way, let me know ;)

So, I hope this helped an you like my corrected answer :)

Jimmy

Jimmy Koerting
  • 1,231
  • 1
  • 14
  • 27
  • Yes, I think this is getting at what I need. So, I'm not very well versed in working with the Address Book. What goes in the multiValue variable? Is it an array, or a dictionary? Basically, I need to tell it to use a specific country code, then get the label for all the other fields. How can I do that? – thephatp Sep 01 '11 at 02:40
  • I was so excited to try this out, b/c I thought it was it, but alas, it wasn't quite what I needed. The last section of your code with key: %@ -> %@ is really the heart of what I'm looking for, but it isn't giving what I hoped. Here is an example for one of my contacts. Country Code = US... The "keys" in this instance are "Street" "City" "State" "ZIP" "Country", all with the expected "values". Example 2 (another contact): Country Code = "GB" (United Kingdom). Keys are the same "Street" "City" "State" "ZIP" "Country". My next comment will describe what I'm hoping to get. – thephatp Sep 01 '11 at 14:22
  • I was HOPING to get the following for United Kingdom: "Street" "City" "County" "Post Code" "Country". Notice the subtle but important difference in the text (in an actual contact in the address book, this is the placeholder text of the UITextField). – thephatp Sep 01 '11 at 14:25
  • Once I figure out how to get those items (placeholder text), I should then be able to get the "localized" version by changing the settings of the phone's "Language" (Settings > General > International > Language). However, if there is a better programmatic way of getting that information as well, that would be a LOT faster than changing the settings over and over again. – thephatp Sep 01 '11 at 14:27
  • One last thing...thank you so much for trying to help. I GREATLY appreciate it. – thephatp Sep 01 '11 at 14:27
  • @thehatp, ok, I guess now I finally got you. You hoped that setting the Country Code to the entry gives you different field names? That is not the case. In general my first post showed you the only available constants for address values. And therefor "kABPersonAddressStateKey" is used for both 'County' in GB and State in USA. In other countries this field is unused. I'm really sorry that I don't know how to get all possible placeholders. But to be honest - you don't need it to know, if you going to create an iPhone app. In the next comment I tell you why :) – Jimmy Koerting Sep 01 '11 at 18:13
  • There are to reasons why it is not a good idea, that you collect all possible placeholders (I will call them labels...), cause you'll never know if they will add/remove/change an country sometimes. And it's to much work ;) You started your question with that: "I am trying to present a way for the user to enter addresses in a location based application, and I want it to look exactly like the address from the iPhone Contacts/Address Book." - No problem, if you application starts, temporary create a new record in the addressbook (I hate the limited comments.. one will follow...) – Jimmy Koerting Sep 01 '11 at 18:17
  • ... and fill it with all possible constants (see the addresses constants from above(ABPerson.h)) and than ask for the labels as I showed you in my answer. Now you have ALL possible labels in the language the current user is using ;) This will work on changed countries in the future, too. I am happy if my answer did help you, please rate it here - I need to earn some points :D – Jimmy Koerting Sep 01 '11 at 18:20
  • @ Jimmy, that's a great point, and great suggestion. Only problem is that I find different results than what you are expecting. Go to your Contacts and add a new one. Set the country to "United States". Notice the "labels" (placeholder text) for each field. Now set the country to "United Kingdom". Notice the "labels" (placeholder text) is different. Now, create a contact with both countries and any data in each field. You have a US contact and a GB (United Kingdom) contact. Now run your code above and notice the output of "NSLog(@"key: %@ -> value: %@", innerKeyLabel, value);"... – thephatp Sep 01 '11 at 21:58
  • ...You see now that the output, say for the Zip code is "ZIP" for both, instead of "ZIP" for US and "Post Code" for United Kingdom. I do think you understand what I'm going for, we just don't quite have the solution yet. Feels like we are so close... – thephatp Sep 01 '11 at 22:00
  • @thephatp Just reread my last comment. Your logging is correct, cause your settings seems to be US. You can't get ALL possible locales in one run (these depends on the users system settings). You just can get the locale labels of the user that uses your app at runtime. And that's all you need to create a view at runtime that has exactly the labels he knows from his iphone addressbook. – Jimmy Koerting Sep 05 '11 at 08:47