Now i ve got this code:
-(void)getContacts{
ABAddressBookRef currentAddressBook = ABAddressBookCreate();
if (currentAddressBook) {
CFArrayRef allBook = ABAddressBookCopyArrayOfAllPeople(currentAddressBook);
if (allBook) {
for (int index=0; index < CFArrayGetCount(allBook); index++){
ABRecordRef currentPerson = CFArrayGetValueAtIndex(allBook, index);
NSString *firstName = (__bridge NSString *)ABRecordCopyValue(currentPerson, kABPersonFirstNameProperty);
NSString *lastName = (__bridge NSString *)ABRecordCopyValue(currentPerson, kABPersonLastNameProperty);
NSData *imageData = (__bridge NSData*)ABPersonCopyImageDataWithFormat(currentPerson, kABPersonImageFormatThumbnail);
NSMutableArray *tempArrayForPhones = [[NSMutableArray alloc] init];
ABMultiValueRef phoneNumbersMultiValue = ABRecordCopyValue(currentPerson, kABPersonPhoneProperty);
for(CFIndex counter = 0; counter < ABMultiValueGetCount(phoneNumbersMultiValue); counter++){
CFStringRef currentLabel = ABMultiValueCopyLabelAtIndex(phoneNumbersMultiValue, counter);
NSString *phoneLabel =(__bridge NSString*) ABAddressBookCopyLocalizedLabel(currentLabel);
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(phoneNumbersMultiValue, counter);
currentLabel = ABMultiValueCopyLabelAtIndex(phoneNumbersMultiValue, counter);
phoneLabel =(__bridge NSString*) ABAddressBookCopyLocalizedLabel(currentLabel);
NSString *phoneNumber = (__bridge NSString *)phoneNumberRef;
CFRelease(phoneNumberRef);
CFRelease(currentLabel);
NSDictionary *tempDictForPhonew = [NSDictionary dictionaryWithObjectsAndKeys:phoneNumber,#"number",
phoneLabel,#"label",
nil];
[tempArrayForPhones addObject:tempDictForPhonew];
}
NSDictionary *dictionaryWithAddressBook = [NSDictionary dictionaryWithObjectsAndKeys:firstName,#"firstName",
lastName,#"lastName",
imageData,#"image",
tempArrayForPhones,#"phones",
nil];
tempArrayForPhones = nil;
[dataArray addObject:dictionaryWithAddressBook];
CFRelease(phoneNumbersMultiValue);
}
CFRelease(allBook);
}
CFRelease(currentAddressBook);
}
}
all works fine on simulator, i get array with dictionary, which contains all fields i need. But when i run code on real device(iOS 5.1) dictionary got only first name and last name.
I tried to do some nslog around initializing dictionary, and temp dictionary with phones and image data existed, but not in final dataArray. Whats wrong?
Problem solved; It was a mistake in parsing address book; I forgot to handle contacts without first name.
Related
First a quick background, I hired a developer to build my app however I know that I learn better by dissecting existing code so my goal is to learn more about iOS programming this way rather than paying the developer to fix.
Now, the app is crashing when I am importing a contact from the address book into the app. I have noticed that the app crash happens when I import a contact from the iOS Facebook contacts but not other groups of contacts. What could be causing this?
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000
Triggered by Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
Research tells me that the app is trying to call something it does not have access to but not sure where to go from here.
Here is the area of code that I believe is causing the issue. (This action).
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
[_addressBookController dismissViewControllerAnimated:YES completion:nil];
}
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
_arrContactsData =[[NSMutableArray alloc] init];
NSMutableDictionary *contactInfoDict = [[NSMutableDictionary alloc]
initWithObjects:#[#"", #"", #"", #"", #"", #"", #"", #"", #""]
forKeys:#[#"firstName", #"lastName", #"mobileNumber", #"homeNumber", #"homeEmail", #"workEmail", #"address", #"zipCode", #"city"]];
// Use a general Core Foundation object.
CFTypeRef generalCFObject = ABRecordCopyValue(person, kABPersonFirstNameProperty);
// Get the first name.
if (generalCFObject) {
[contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:#"firstName"];
CFRelease(generalCFObject);
}
// Get the last name.
generalCFObject = ABRecordCopyValue(person, kABPersonLastNameProperty);
if (generalCFObject) {
[contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:#"lastName"];
CFRelease(generalCFObject);
}
// Get the phone numbers as a multi-value property.
ABMultiValueRef phonesRef = ABRecordCopyValue(person, kABPersonPhoneProperty);
for (int i=0; i<ABMultiValueGetCount(phonesRef); i++) {
CFStringRef currentPhoneLabel = ABMultiValueCopyLabelAtIndex(phonesRef, i);
CFStringRef currentPhoneValue = ABMultiValueCopyValueAtIndex(phonesRef, i);
if (CFStringCompare(currentPhoneLabel, kABPersonPhoneMobileLabel, 0) == kCFCompareEqualTo) {
[contactInfoDict setObject:(__bridge NSString *)currentPhoneValue forKey:#"mobileNumber"];
}
if (CFStringCompare(currentPhoneLabel, kABHomeLabel, 0) == kCFCompareEqualTo) {
[contactInfoDict setObject:(__bridge NSString *)currentPhoneValue forKey:#"homeNumber"];
}
CFRelease(currentPhoneLabel);
CFRelease(currentPhoneValue);
}
CFRelease(phonesRef);
// Get the e-mail addresses as a multi-value property.
ABMultiValueRef emailsRef = ABRecordCopyValue(person, kABPersonEmailProperty);
for (int i=0; i<ABMultiValueGetCount(emailsRef); i++) {
CFStringRef currentEmailLabel = ABMultiValueCopyLabelAtIndex(emailsRef, i);
CFStringRef currentEmailValue = ABMultiValueCopyValueAtIndex(emailsRef, i);
if (CFStringCompare(currentEmailLabel, kABHomeLabel, 0) == kCFCompareEqualTo) {
[contactInfoDict setObject:(__bridge NSString *)currentEmailValue forKey:#"homeEmail"];
}
if (CFStringCompare(currentEmailLabel, kABWorkLabel, 0) == kCFCompareEqualTo) {
[contactInfoDict setObject:(__bridge NSString *)currentEmailValue forKey:#"workEmail"];
}
CFRelease(currentEmailLabel);
CFRelease(currentEmailValue);
}
CFRelease(emailsRef);
// Get the first street address among all addresses of the selected contact.
ABMultiValueRef addressRef = ABRecordCopyValue(person, kABPersonAddressProperty);
if (ABMultiValueGetCount(addressRef) > 0) {
NSDictionary *addressDict = (__bridge NSDictionary *)ABMultiValueCopyValueAtIndex(addressRef, 0);
[contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressStreetKey] forKey:#"address"];
[contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressZIPKey] forKey:#"zipCode"];
[contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressCityKey] forKey:#"city"];
}
CFRelease(addressRef);
// If the contact has an image then get it too.
if (ABPersonHasImageData(person)) {
NSData *contactImageData = (__bridge NSData *)ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);
[contactInfoDict setObject:contactImageData forKey:#"image"];
}
// Initialize the array if it's not yet initialized.
if (_arrContactsData == nil) {
_arrContactsData = [[NSMutableArray alloc] init];
}
// Add the dictionary to the array.
[_arrContactsData addObject:contactInfoDict];
// Reload the table view data.
// Dismiss the address book view controller.
[_addressBookController dismissViewControllerAnimated:YES completion:nil];
NSDictionary *info = [_arrContactsData objectAtIndex:0];
self.txfFirstName.text = [info objectForKey:#"firstName"];
self.txfLastName.text = [info objectForKey:#"lastName"];
self.txfMobile.text =[info objectForKey:#"mobileNumber"];
self.txfEmail.text =[info objectForKey:#"homeEmail"];
NSLog(#"Info %#",info);
return NO;
}
It looks like Facebook labels its email contacts weird in the address book, so that when you query the labels it returns a nil value, which will give you your bad access error and the crash
personally i put an if statement around it and just ignored all nil addresses as i only wanted work addresses anyway, see following example in your code (i haven't checked this but it should work)
After "analyzing" my previous answer I noticed that it actually reported a memory leak so i moved the if around a bit and with the updated code below its gone.
for (int i=0; i<ABMultiValueGetCount(emailsRef); i++) {
CFStringRef currentEmailLabel = ABMultiValueCopyLabelAtIndex(emailsRef, i);
CFStringRef currentEmailValue = ABMultiValueCopyValueAtIndex(emailsRef, i);
if (currentEmailLabel != nil) {
if (CFStringCompare(currentEmailLabel, kABWorkLabel, 0) == kCFCompareEqualTo) {
[self.contactInfoDict setObject:(__bridge NSString *)currentEmailValue forKey:#"workEmail"];
}
CFRelease(currentEmailLabel);
}
CFRelease(currentEmailValue);
}
CFRelease(emailsRef);//END OF IF WRAPPER
if you want the emails and don't care that they are Facebook I imagine you could just do a nil check on the label and relabel it or just ignore the label compare completely.
Guys am working on a calling app for Ios,so naturally i have a dial-pad integrated.
The problem am facing in to get the contact details from number dialed from my dial-pad which i am showing in other screen.
Here is my code
+(PhoneContactModel*) getContactFrom:(NSString *)calledPhoneNumber{
PhoneContactModel *contact=[[PhoneContactModel alloc]init];
ABAddressBookRef addressBook = [AppUtils getCompatibleAdressBook];
CFArrayRef all = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex n = ABAddressBookGetPersonCount(addressBook);
for( int i = 0 ; i < n ; i++ )
{
ABRecordRef ref = CFArrayGetValueAtIndex(all, i);
ABMultiValueRef phones = (ABMultiValueRef)ABRecordCopyValue(ref, kABPersonPhoneProperty);
for(CFIndex j = 0; j < ABMultiValueGetCount(phones); j++)
{
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(phones, j);
//CFRelease(phones);
NSString *phoneNumber = (__bridge NSString *)phoneNumberRef;
NSLog(#"apputil number %#",[AppUtils getNumberSanatized:phoneNumber]);
if ([phoneNumber isEqualToString:calledPhoneNumber]){
NSLog(#"apputil number matched %#",[AppUtils getNumberSanatized:phoneNumber]);
contact.strFullName = (__bridge_transfer NSString *) ABRecordCopyValue(ref, kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString *) ABRecordCopyValue(ref, kABPersonLastNameProperty);
contact.strFullName=[contact.strFullName stringByAppendingString:#" "];
if (lastName!=nil){
contact.strFullName=[contact.strFullName stringByAppendingString:lastName];
}
contact.imgContactImge=[AppUtils imageForContact:ref];
contact.strNumber=phoneNumber;
return contact;
}
}
}
contact.strFullName = calledPhoneNumber;
return contact;
}
The problem appears like if i have a Contact A with number 64xxxx... and i dial +164xxxx.. from my dial-pad i don't get the contact details, Also you can see that in the method above i have to run a loop to find the matching contact even if there is a match, so is there a better method out there to do the same
Thanks
I created a better solution
it can be seen here http://codesnight.blogspot.in/2013/11/ios-acces-contacts-from-number.html
I am using the following code to get mobile number (i.e. 1st phone number) of all contacts in my address book. I want to store all those number into NSArray
self.dataSource = [[NSMutableArray alloc]init]; // dataSouce is delared in .h file
ABAddressBookRef addressBook = ABAddressBookCreate();
NSMutableArray *allPeople = (__bridge NSMutableArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
int nPeople = ABAddressBookGetPersonCount(addressBook);
for(int i=0; i < nPeople; i++ ){
ABRecordRef person = (__bridge ABRecordRef)([allPeople objectAtIndex:i]);
NSString *name = #"";
if(ABRecordCopyValue(person, kABPersonFirstNameProperty) != NULL)
name = [[NSString stringWithFormat:#"%#", ABRecordCopyValue(person, kABPersonFirstNameProperty)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
[_dataSource addObject: name];
ABMultiValueRef phoneNumberProperty = ABRecordCopyValue(person, kABPersonPhoneProperty);
_phoneNumbers = (__bridge NSArray*)ABMultiValueCopyValueAtIndex(phoneNumberProperty, 0);
CFRelease(phoneNumberProperty);
NSLog(#"Phone numbers = %#", _phoneNumbers);
}
but when I use NSLog(#"Phone numbers = %#", _phoneNumbers); the output comes out as
2012-11-07 15:31:06.116 contacts[4938:12b03] Phone numbers = 1 (800) 111-1111
2012-11-07 15:31:06.117 contacts[4938:12b03] Phone numbers = (222) 355-5668
2012-11-07 15:31:06.118 contacts[4938:12b03] Phone numbers = (910) 192-0192
I want the output in NSArray like
Phone numbers = (
"1 (800) 111-1111",
"(222) 355-5668",
"(910) 192-0192"
)
how can I do this ?
you Can Do it Simply like this
self.dataSource = [[NSMutableArray alloc]init]; // dataSouce is delared in .h file
ABAddressBookRef addressBook = ABAddressBookCreate();
NSMutableArray *allPeople = (__bridge NSMutableArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
int nPeople = ABAddressBookGetPersonCount(addressBook);
NSMutableArray *arrContacts = [[NSMutableArray alloc] init];
for(int i=0; i < nPeople; i++ ){
ABRecordRef person = (__bridge ABRecordRef)([allPeople objectAtIndex:i]);
NSString *name = #"";
if(ABRecordCopyValue(person, kABPersonFirstNameProperty) != NULL)
name = [[NSString stringWithFormat:#"%#", ABRecordCopyValue(person, kABPersonFirstNameProperty)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
[_dataSource addObject: name];
ABMultiValueRef phoneNumberProperty = ABRecordCopyValue(person, kABPersonPhoneProperty);
_phoneNumbers = (__bridge NSArray*)ABMultiValueCopyValueAtIndex(phoneNumberProperty, 0);
CFRelease(phoneNumberProperty);
NSLog(#"Phone numbers = %#", _phoneNumbers);
[arrContacts addObject:_phoneNumbers];
}
NSLog(#"Phone numbers : %#",arrContacts);
I found this via Google and was using this address book code as an example, but found a mistake that I'd correct for others coming here.
When using ABMultiValueCopyValueAtIndex it returns a type CFTypeRef. That function gets just the phone number at that index (in this case at index 0) which in the case of the phone number is a CFStringRef that shouldn't be cast to an NSArray*.
ABMultiValueCopyArrayOfAllValues would be the proper call to get the array of all phone numbers stored in a contact. That can be stored into an array and logged like Sarafaraz is doing in his answer.
NSArray *_phoneNumbers = (__bridge NSArray*)ABMultiValueCopyArrayOfAllValues(phoneNumberProperty);
If you just want to get the first phone number you would do this:
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(phoneNumberProperty, 0);
NSString *phoneString = (__bridge NSString*)phoneNumberRef;
I am using an ABPeoplePickerNavigationController to collect an array of email addresses and phone numbers from a contact book entry. 90% of the time it works fine but a few testers are reporting crashes. The crash report says it is crashing at CFRelease ... Not sure why considering I believe my code is correct. Take a look:
ABProfile *selectedUser = [[ABProfile alloc]init];
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
NSArray *emailArray;
if (ABMultiValueGetCount(emails) > 0) {
emailArray = (__bridge_transfer NSArray *)ABMultiValueCopyArrayOfAllValues(emails);
}
CFRelease(emails);
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
NSMutableArray *phonesArray = [[NSMutableArray alloc]initWithCapacity:1];
for(CFIndex j = 0; j < ABMultiValueGetCount(phones); j++)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc]initWithCapacity:1];
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(phones, j);
CFStringRef locLabel = ABMultiValueCopyLabelAtIndex(phones, j);
NSString *phoneLabel =(__bridge_transfer NSString*) ABAddressBookCopyLocalizedLabel(locLabel);
CFRelease(locLabel);
[dict setValue:phoneLabel forKey:#"label"];
NSString *phoneNumber = (__bridge_transfer NSString *)phoneNumberRef;
[dict setValue:phoneNumber forKey:#"number"];
[phonesArray addObject:dict];
}
selectedUser.phones = phonesArray;
CFRelease(phones);
ABMultiValueCopyLabelAtIndex can return NULL if not found, and CFRelease(NULL) will crash. I would cheek to ensure locLabel exists before doing anything with it.
I'm able to pull the first name and the last name of the contact list from iphone sdk, however I'm unable to fetch the phone number from it. I'm getting the error if I try other way, and the usual way I'm getting other stuff with phone number here is the details with the code:
- (IBAction)buttonmessage {
ABAddressBookRef addressBook = ABAddressBookCreate(); // create address book reference object
NSArray *abContactArray = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook); // get address book contact array
NSInteger totalContacts = [abContactArray count];
for(NSUInteger loop= 0 ; loop < totalContacts; loop++)
{
ABRecordRef record = (__bridge ABRecordRef)[abContactArray objectAtIndex:loop]; // get address book record
if(ABRecordGetRecordType(record) == kABPersonType) // this check execute if it is person group
{
ABRecordID recordId = ABRecordGetRecordID(record); // get record id from address book record
recordIdString = [NSString stringWithFormat:#"%d",recordId]; // get record id string from record id
firstNameString = (__bridge NSString*)ABRecordCopyValue(record,kABPersonFirstNameProperty); // fetch contact first name from address book
lastNameString = (__bridge NSString*)ABRecordCopyValue(record,kABPersonLastNameProperty); // fetch contact last name from address book
NSString *phnumber = (__bridge NSString *)ABRecordCopyValue(record, kABPersonPhoneProperty);
myArray2 = [NSArray arrayWithObjects:firstNameString,lastNameString,phnumber,nil];
NSString *m12=[NSString stringWithFormat:#"%#,%#,%#",[myArray2 objectAtIndex:0],[myArray2 objectAtIndex:1],[myArray2 objectAtIndex:2]];
}
Output:
Abdullah,Rashed,ABMultiValueRef 0x80426a0 with 1 value(s)
0: _$!<Mobile>!$_ (0x8042da0) - 0550979691 (0x8042dc0)
2012-05-07 14:43:06.670 Firstphase[2914:207] Hussain,Mahmood,ABMultiValueRef 0x80442d0 with 1 value(s)
0: _$!<Mobile>!$_ (0x8044290) - 055979896 (0x80442b0)
2012-05-07 14:43:06.671 Firstphase[2914:207] Nasir,Jilaani,ABMultiValueRef 0x8046070 with 1 value(s)
0: _$!<Mobile>!$_ (0x8046000) - 055982391 (0x8046020)
2012-05-07 14:43:06.673 Firstphase[2914:207] Ghulam,Basith,ABMultiValueRef 0x8046850 with 1 value(s)
0: _$!<Mobile>!$_ (0x8046810) - 055871943 (0x8046830)
However if you take a close look i'm able to get the firstname and last name without any extra stuff. But I'm not able to get the phone numbers in the same way.
ABMutableMultiValueRef multi;
int multiCount = 0;
multi = ABRecordCopyValue(record, kABPersonPhoneProperty);
multiCount = ABMultiValueGetCount(multi);
for (int i = 0; i < multiCount; i++) {
phoneNumber = (NSString * ) ABMultiValueCopyValueAtIndex(multi, i);
[someArray addObject: phoneNumber];
}
check this:
Your problem may solve with this answer or this.
As of I understand ABRecordCopyValue(ref, kABPersonPhoneProperty) returns some array value. And you are trying to get a String thats why you may face the issue. I didn't tried this solution but think it will work.
Hope this helps.
Try the following:
ABMultiValueRef phoneNumberProperty = ABRecordCopyValue(record, kABPersonPhoneProperty);
NSArray *phoneNumbers = (NSArray*)ABMultiValueCopyArrayOfAllValues(phoneNumberProperty);
for (id number in phoneNumbers) {
// do whatever you want
}
The following code will retrieve all the phone numbers from the contact list:-
ABAddressBookRef addressBook = ABAddressBookCreate( );
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
CFIndex nPeople = ABAddressBookGetPersonCount( addressBook );
for ( int i = 0; i < nPeople; i++ )
{
ABRecordRef ref = CFArrayGetValueAtIndex( allPeople, i );
ABMultiValueRef phoneNumberProperty = ABRecordCopyValue(ref, kABPersonPhoneProperty);
NSArray* phoneNumbers = (NSArray*)ABMultiValueCopyArrayOfAllValues(phoneNumberProperty);
CFRelease(phoneNumberProperty);
[phoneNumbers release];
}
It will work..
-(void)displayPerson
{
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (addressBook != nil)
{
NSLog(#"Succesful.");
NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSUInteger i = 0;
for (i = 0; i < [allContacts count]; i++)
{
Person *person = [[Person alloc] init];
ABRecordRef contactPerson = (__bridge ABRecordRef)allContacts[i];
NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson, kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson, kABPersonLastNameProperty);
NSString *fullName = [NSString stringWithFormat:#"%# %#", firstName, lastName];
NSString *phone=nil;
ABMultiValueRef phoneNumbers = ABRecordCopyValue(contactPerson, kABPersonPhoneProperty);
if (ABMultiValueGetCount(phoneNumbers) > 0) {
phone = (__bridge_transfer NSString*)
ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
} else {
phone = #"[None]";
}
person.fullname = fullName;
person.phoneNum=phone;
[self.tableData addObject:person];
person=nil;
}
CFRelease(addressBook);
}
}