Blank field showing up on iPhone AddressBook, how to debug? - iphone

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;

Related

Core Data table to NSArray

I have the following Array which retrieved from the Core Data :
NSArray *dataArray = [context executeFetchRequest:request error:&error];
so I wrote this code to get each row data individually to send it to REST API:
for (NSString *str in dataArray) {
NSString *name =[dataArray valueForKey:#"name"];
NSString *dob = [dataArray valueForKey:#"dob"];
int gender =[[dataArray valueForKey:#"gender"]integerValue];
NSString *childId =[dataArray valueForKey:#"id"];
int response = [network sendName:name withDateOfBirth:dob andGender:gender forID:childId];
if (response == 200) {
// [self performSelectorOnMainThread:#selector(postSuccess) withObject:nil waitUntilDone:YES];
NSLog(#"Success");
}
}
but it's not working, because I couldn't know how data is stored in each index in the array!!
Please help, and if I am not doing this correctly please tell me a better way to do it.
Thanks.
NSString *name =[dataArray valueForKey:#"name"];
This doesn't do what you think it'll do. valueForKey:, when sent to an array, returns an array of the values corresponding to the given key for all the items in the array. So, that line will assign an array of the "name" values for all the items in dataArray despite the fact that you declared name as a NSString. Same goes for the subsequent lines.
What you probably want instead is:
for (NSManagedObject *item in dataArray) {
NSString *name = [item valueForKey:#"name"];
...
Better, if you have a NSManagedObject subclass -- let's call it Person representing the entity you're requesting, you can say:
for (Person *person in dataArray) {
NSString *name = person.name;
...
which leads to an even simpler version:
for (Person *person in dataArray) {
int response = [network sendName:person.name
withDateOfBirth:person.dob
andGender:person.gender
forID:person.id];
although I'd change the name of that method to leave out the conjunctions and prepositions. -sendName:dateOfBirth:gender:id: is enough, you don't need the "with", "and", and "for."

how to get records of contacts from NSMutableArray in iPhone

I have an array of dictionary for contact details. I am trying to add that record in ABRecordRef, but I don't understand how it works. Here is my code:
for (int i = 0; i <= [contactArray count]; i++)
{
ABRecordRef person = (ABRecordRef)[contactArray objectAtIndex:i];
ABAddressBookAddRecord(addressBook, group, &error);
ABAddressBookSave(addressBook, &error);
}
I am trying to add this contact records into group using ABGroupAddMember. Now how can I get the records from NSMutableArray. Any help will be greatly appreciated. Thank you.
there is no built-in functionality for that. you will have to create an empty record, THEN get all the fields from the dict and THEN add those to the record and safe it
that's annoying manual work... :D
I'd go with Erica's ABContactHelper: https://github.com/erica/ABContactHelper
then it is only
for(NSDictionary *d in recordArray) {
ABContact *contact = [ABContact contactWithDictionary:d];
ABGroupAddMember(theGroup, contact.record);
}
if you like manual:
for(NSDictionary *d in recordArray) {
ABRecordRef person = ABPersonCreate();
for(NSString *k in d.allKeys) {
id v = d[k];
//HERE call ABRecordSetValue with the right params depending on if the value is a NSString or NSArray or an image
}
ABGroupAddMember(theGroup, contact.record);
CFRelease(person);
}
disclaimer: typed inline but should be ok

Iphone AddressBook Framework - Person attributes

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.

How to programmatically get address placeholder text from iphone addressbook?

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

kABPersonAddressProperty causes crashes under ABAddressBookGetPersonWithRecordID

I am trying to be an ABRecordRef that represents the contact info of a person from addressbook. I built two functions that calls a function to fill in a personal data structure with the info in ABRecordRef.
Here comes the function declarations for the three functions:
+ (NSMutableArray*) getAllContactProfiles{
NSMutableArray *listOfProfile = [[NSMutableArray alloc] init];
//---get the contact information for the api
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeopleInAddressBook = ABAddressBookGetPersonCount(addressBook);
//<- Here I loop through all the contacts and pass the ABRecordRef into the following function
//---release the variables---
CFRelease(addressBook);
CFRelease(people);
[listOfProfile autorelease];
return listOfProfile;
}
The Following Function
+ (MSProfileEntry*) getPersonProfileThroughABRecordRef:(ABRecordRef) person{
MSProfileEntry *mockProfile;
ABRecordID recID=ABRecordGetRecordID(person);
//get the user name
CFStringRef firstName;
firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);//it goes wrong here!
CFStringRef lastName;
lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
//bla bla bla.. the rest of the code
}
Everything goes very well. However, when I try to get the ABRecordRef through ABAddressBookGetPersonWithRecordID like it is in the next method:
The Next Method
+ (MSProfileEntry*) getPersonProfileThroughContactId:(NSInteger*)contactId{
ABAddressBookRef addressBook = ABAddressBookCreate();
ABRecordRef person =
ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID)contactId);
CFRelease(addressBook);
if (person == nil) {
return nil;
}
return [MSContactUtil getPersonProfileThroughABRecordRef:person];
}
The whole app crashes on line:ABRecordCopyValue(person, kABPersonFirstNameProperty);.
The problem now is that ABRecordCopyValue(person, kABPersonFirstNameProperty); works perfectly fine with ABAddressBookCopyArrayOfAllPeople but causes the app to crash with ABAddressBookGetPersonWithRecordID.
Does anyone have any clue how to solve this problem? I really don't want to loop through entire contact base just to look for a contact.
It turned out to be a memory issue. I forgot to retain the "addressBook". By the time I executed the following line:
firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
The "addressBook" had been cleaned out already. Somehow we still need "addressBook" while querying for the detail information in "person".
So, remember to put in the following line, and you will be safe.
CFRetain(addressBook);
Two things:
You pass (NSInteger*)contactId to getPersonProfileThroughContactId and after that you call ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID)contactId);. Actually you pass an address of the integer that holds the contact id and not the id itself...
You check if (person == nil), BUT person may not be nil - you should compare with NULL. I believe that it IS NULL in your case (because of my previous point).
These 2 things together cause the crash.
Just pass an integer as is - not its address...
EDIT:
Like this:
+ (MSProfileEntry*)getPersonProfileThroughContactId:(NSInteger)contactId