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).
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
Related
I am trying to find out which attributes a complete "Person Record" in the iPhone Addressbook has by default.
It must be hidden somewhere in the API
https://developer.apple.com/library/content/documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Introduction.html#//apple_ref/doc/uid/TP40007744
https://developer.apple.com/documentation/addressbook#//apple_ref/doc/uid/TP40007210
but I didn't find a list so far.
Has anybody a list of the attributes: Name, Prename, email, tel and possible "hidden" field like entry created
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
ABAddressBookRef addressBook = ABAddressBookCreate();
NSArray *array= (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
for (id persion in array)
{
ABRecordRef record = (ABRecordRef)persion;
NSString* firstName=(NSString *)ABRecordCopyValue(record, kABPersonFirstNameProperty);
NSString* lastName=(NSString *)ABRecordCopyValue(record, kABPersonLastNameProperty);
//do something
[firstName release];
[lastName release];
ABMultiValueRef mulPhone = (ABMultiValueRef) ABRecordCopyValue(record, kABPersonPhoneProperty) ;
int count = ABMultiValueGetCount(mulPhone);
for(CFIndex i=0 ; i < count ; i++)
{
NSString* phoneLabel = (NSString*) ABMultiValueCopyLabelAtIndex(mulPhone, i) ;
NSString* cellPhone =(NSString*) ABMultiValueCopyValueAtIndex(mulPhone, i) ;
//do something
[phoneLabel release] ;
[cellPhone release];
}
CFRelease(mulPhone) ;
ABMultiValueRef mulAddress =(ABMultiValueRef) ABRecordCopyValue(record, kABPersonAddressProperty) ;
count = ABMultiValueGetCount(mulAddress);
for(CFIndex i=0 ; i< count ; i++)
{
NSString* addressLabel = (NSString*) ABMultiValueCopyLabelAtIndex(mulAddress, i) ;
CFDictionaryRef dict = (CFDictionaryRef)ABMultiValueCopyValueAtIndex(mulAddress, i);
NSString* homeStreet = (NSString*)CFDictionaryGetValue(dict, kABPersonAddressStreetKey);
NSString* homeCity = (NSString*)CFDictionaryGetValue(dict, kABPersonAddressCityKey);
NSString* homeCountry = (NSString*)CFDictionaryGetValue(dict, kABPersonAddressCountryKey);
//do something
CFRelease(dict) ;
[addressLabel release];
}
CFRelease(mulAddress) ;
NSString* company = (NSString*)ABRecordCopyValue(record, kABPersonOrganizationProperty);
if (company) {
//do something
}
[company release];
ABMultiValueRef mulEmail = (ABMultiValueRef)ABRecordCopyValue(record, kABPersonEmailProperty) ;
count = ABMultiValueGetCount(mulEmail);
for(CFIndex i=0 ; i< count; i++)
{
NSString* emailLabel = (NSString*)ABMultiValueCopyLabelAtIndex(mulEmail, i) ;
NSString* email = (NSString*) ABMultiValueCopyValueAtIndex(mulEmail, i) ;
//do something
[emailLabel release];
[email release];
}
CFRelease(mulEmail) ;
}
[array release];
CFRelease(addressBook);
[pool release];
Default attributes for a Person: https://developer.apple.com/documentation/addressbook/address_book_objective_c_constants/default_person_properties
kABFirstNameProperty: First name.
kABLastNameProperty: Last name.
kABFirstNamePhoneticProperty: Phonetic representation of the first name.
kABLastNamePhoneticProperty: Phonetic representation of the last name.
kABNicknameProperty: Nickname.
kABMaidenNameProperty: Maiden name.
kABBirthdayProperty: Birth date.
kABBirthdayComponentsProperty: Birth date as date components.
kABOrganizationProperty: Company name.
kABJobTitleProperty: Job title.
kABHomePageProperty: Home web page.
kABURLsProperty: Web pages.
kABCalendarURIsProperty: Calendar URIs.
kABEmailProperty: Email addresses.
kABAddressProperty: Street addresses.
kABOtherDatesProperty: Dates associated with a person.
kABOtherDateComponentsProperty: Dates associated with a person, as date components.
kABRelatedNamesProperty: Names of people related to a person.
kABDepartmentProperty: Department name.
kABPersonFlags: Property that specifies the name ordering and configuration of a record in the Address Book application. See Person Flags.
kABPhoneProperty: Generic phone number.
kABInstantMessageProperty: Instant messaging ID.
kABNoteProperty: Notes.
kABSocialProfileProperty: Social network profile.
kABMiddleNameProperty: Middle name.
kABMiddleNamePhoneticProperty: Phonetic representation of the middle name.
kABTitleProperty: Title, such as “Mr.,” “Mrs.,” “General,” “Cardinal,” or “Lord.”
kABSuffixProperty: Suffix, such as “Sr.,” “Jr.,” “III.,” or “Esq.”
And because a Person is a Record, it also has: https://developer.apple.com/documentation/addressbook/address_book_objective_c_constants/default_record_properties
kABUIDProperty: The unique ID for this record. It’s guaranteed never to change, no matter how much the record changes. If you need to store a reference to a record, use this value.
kABCreationDateProperty: The date when the record was first saved.
kABModificationDateProperty: The date when the record was last saved.
Note, the properties() should return you the list of all properties of a Person.
I am developing app where I would like to search for exchange contact by Name (similar to what phone contacts app does), going through AddressBook API of iOS and also searching net I am still not able to understand how can I use iOS address book api to search exchange contact.
I could only find that Address book provided information that an ABSource is searchable but does not provide how to search. If any body can help it is much appreciated. Thank you Very much in advance.. I have been struggling on this for quite a long time now..
I also tried to customize ABPeoplePicker but had no much help.
The approach I took to solving this was to find the ABSource records that I wanted, then use those to get the ABPerson records in the source, then build up some data structures and filter them with NSPredicate. A bit convoluted perhaps but it seems to work.
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef sources = ABAddressBookCopyArrayOfAllSources(addressBook);
CFIndex sourcesCount = CFArrayGetCount(sources);
ABRecordRef sourceToSearch = NULL;
for (CFIndex index = 0; index < sourcesCount; index++)
{
ABRecordRef record = (ABRecordRef)CFArrayGetValueAtIndex(sources, index);
NSNumber *sourceTypeNumber = (__bridge NSNumber *)(CFNumberRef)ABRecordCopyValue(record, kABSourceTypeProperty);
ABSourceType sourceType = [sourceTypeNumber intValue];
if (sourceType == 4) //this was the only source type with people on my phone, I guess you'll use kABSourceTypeExchange instead
{
sourceToSearch = record;
break;
}
}
CFArrayRef peopleInRecord = (CFArrayRef)ABAddressBookCopyArrayOfAllPeopleInSource(addressBook, sourceToSearch);
CFIndex peopleCount = CFArrayGetCount(peopleInRecord);
NSMutableArray *peopleDictionaries = [NSMutableArray array];
for (CFIndex index = 0; index < peopleCount; index++)
{
ABRecordRef personRecord = CFArrayGetValueAtIndex(peopleInRecord, index);
ABRecordID recordID = ABRecordGetRecordID(personRecord);
NSString *personName = (__bridge NSString *)(CFStringRef)ABRecordCopyValue(personRecord, kABPersonFirstNameProperty);
if (personName)
{
NSDictionary *personDictionary = #{ #"recordID" : [NSNumber numberWithInt:recordID], #"name" : personName };
[peopleDictionaries addObject:personDictionary];
}
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K like %#",#"name",#"Kyle"];
NSArray *kyles = [peopleDictionaries filteredArrayUsingPredicate:predicate];
NSLog(#"filtered dictionarys = %#",kyles);
/*
2012-08-27 17:26:24.679 FunWithSO[21097:707] filtered dictionaries = (
{
name = Kyle;
recordID = 213;
}
)*/
//From here, get the recordID instance and go get your ABPerson Records directly from the address book for further manipulation.
Hope this helps, let me know if you have any questions!
I am writing an app where I need to read the Address Book data to search for some contacts of interest, something similar to what many apps do nowadays (like Viber, Whatsapp, Tango...). I need to do matching so I send the data to a server and reply to the client which contacts have the same app installed on their devices.
I have no problem in the logic or mechanism of the idea, my problem is speed! I was able to do what I want but the process took 27 seconds to finish on iPhone4 with 500 contacts on it. On the same device if we try Viber or Whatsapp (or any similar app) the process takes less than 5 seconds.
My method is very straightforward, I do a for loop and read everything. How can I do the same thing but much faster like other apps?
Here is the code that I use:
//variable definitions
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFMutableArrayRef peopleMutable = CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(people), people);
//sort the contents of the Mutable Array
CFArraySortValues(peopleMutable, CFRangeMake(0, CFArrayGetCount(peopleMutable)), (CFComparatorFunction) ABPersonComparePeopleByName, (void*) ABPersonGetSortOrdering());
//read the Address Book
NSString *fullName, *number;
ABRecordRef record = ABPersonCreate();
ABMutableMultiValueRef multi;
int contactID;
int nameCount=0;//used to count the names in the string to send to server
NSMutableString *strNamesToSend = [[NSMutableString alloc] init];
for(CFIndex i=0; i< CFArrayGetCount(people); i++)
{
record = CFArrayGetValueAtIndex(people, i);
multi = ABRecordCopyValue(record, kABPersonPhoneProperty);
//Contact ID
contactID = (int)ABRecordGetRecordID(record);
//Full Name
fullName = [NSString stringWithFormat:#"%# %#", (NSString *)ABRecordCopyValue(record, kABPersonFirstNameProperty), (NSString *)ABRecordCopyValue(record, kABPersonLastNameProperty)];
fullName = [fullName stringByReplacingOccurrencesOfString:#" (null)" withString:#""];
//fill data into AddressBook Table
if(dbOpen == SQLITE_OK)
{
//pure sqlite3 work to save the names in my app
}
//Get multiple numbers from each user (if any)
for(CFIndex j=0; j<ABMultiValueGetCount(multi); j++)
{
number = (NSString *)ABMultiValueCopyValueAtIndex(multi, j);
nameCount++;
//fill data into AllNumbers Table
if(dbOpen == SQLITE_OK)
{
//another sqlite3 work to save the numbers
}
}
//send to the server every 29 numbers so we don't send all the 500 numbers at once
if(nameCount > 29)
{
//send to server
}
Have you tried profiling your code at all? A profiler should be able to quickly identify the slow parts of your code.
From a very brief inspection, I notice you're counting the array size at each iteration rather than just once. Move it out of your loop:
int count = CFArrayGetCount(people);
for (CFIndex i = 0; i < count; i++)
You don't detail the SQL calls you're making, but the fact you're checking for SQLITE_OK implies that you're opening the database each time through the loop. If this is the case you should move this call outside your loop rather than opening the database each time.
I notice that nameCount isn't being reset, which means that once it reaches 29, your if case will be hit every single time, causing a huge number of network requests.
I am newbie to Address book programming. I want to retrieve all email id's from address book.The issue is below code gets the all data for one record(one person). but When i add more than one contact in address book. it crushes without showing any exception.
Any suggestions? Thanks in advance.
self.pastUrls = [[NSMutableArray alloc] init];
ABAddressBookRef addressBook = ABAddressBookCreate();
NSArray *addresses = (NSArray *) ABAddressBookCopyArrayOfAllPeople(addressBook);
// you could probably do some kind of enumeration but I'm doing old fashoined way
int i;
for(i = 0; i < [addresses count]; i++) {
ABRecordRef record = [addresses objectAtIndex:i];
ABMultiValueRef multiValue = ABRecordCopyValue(record, kABPersonEmailProperty);
NSLog(#"%#",multiValue);
int count = ABMultiValueGetCount(multiValue);
NSLog(#"%d",count);
int j;
for(j = 0; j < count; j++) {
NSString *label = (NSString *)ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(multiValue, i));
NSString *value = (NSString *)ABMultiValueCopyValueAtIndex(multiValue, i);
//NSLog(#"Email for %#: %#", label, value);
[pastUrls addObject:value];
}
}
Regards,
sathish
There are a couple of online tutorials here that should help:
http://iphone.zcentric.com/2008/09/19/access-the-address-book/
https://developer.apple.com/iphone/library/documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/100-Introduction/Introduction.html
Apple's Address Book Programming Guide for iOS includes a sample project that will get you started with general principles for accessing address book data, including email addresses.
The following code creates me an array of all my contacts in my address book by first name and last name. The problem is, I have one contact that keeps showing up with an empty first name and last name. I can't find that contact in my actual address book. Can anyone suggest how to debug this to figure out the source of the mystery ghost contact?
ABAddressBookRef addressBook = ABAddressBookCreate();
NSArray *peopleArray = (NSMutableArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSMutableArray *allNames = [NSMutableArray array];
for (id person in peopleArray) {
NSMutableString *firstName = [(NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty) autorelease];
NSMutableString *lastName = [(NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty) autorelease];
ABMutableMultiValueRef multiValueEmail = ABRecordCopyValue(person, kABPersonEmailProperty);
if (ABMultiValueGetCount(multiValueEmail) > 0) {
NSString *email = [(NSString *)ABMultiValueCopyValueAtIndex(multiValueEmail, 0) autorelease];
}
if (![firstName length]) {
firstName = #"";
}
if (![lastName length]) lastName = #"";
[allNames addObject:[NSString stringWithFormat:#"%# %#", firstName, lastName]];
}
The person type is of type NSCFType. I could easily do something like:
if (![lastName length] && ![firstName length]) continue;
.. and be done with the problem. I'm curious though what entry in my AddressBook is coming up as a ghost. I've tried introspecting the object with gdb, but can't get anything valuable out of it.
I'd like to see all properties for person, but derefing the object to (ABPerson*) doesn't appear to do it.
I've also tried using CFShow(person) that reveals it to be type CPRecord. Can't find further documentation on that, however.
Is there something in gdb I can do to further inspect this particular person object to see where the source of it is coming from?
The entry is probably flagged as an organization record, rather than a person record. In this case you'll have to pull out the organization name rather than the first and last name.
Try looking at the properties for:
kABPersonOrganizationProperty, kABPersonKindProperty
IT is probably a contact that is only an organization
try looking at these properties
These constants implement the person type property (a property of
type kABIntegerPropertyType), which
indicates whether a person record
represents a human being or an
organization.
const ABPropertyID kABPersonKindProperty;
const CFNumberRef kABPersonKindPerson;
const CFNumberRef kABPersonKindOrganization;