ABPeoplePickerNavigationController EXC_BAD_ACCESS collecting emails and phone numbers - ios

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.

Related

how to prevent my app from sending sms to landline numbers

While sending SMS through my app,it should sent it to mobile only but for some contacts when two numbers are there one is landline and other is mobile it sends to landline also.
- (NSMutableArray*)getContactsWithAddressBook:(ABAddressBookRef )addressBook {
contactList = [[NSMutableArray alloc] init];
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for (int i=0;i < nPeople;i++) {
NSMutableDictionary *dOfPerson=[NSMutableDictionary dictionary];
ABRecordRef ref = CFArrayGetValueAtIndex(allPeople,i);
//For username and surname
ABMultiValueRef phones =(__bridge ABMultiValueRef)((__bridge NSString*)ABRecordCopyValue(ref, kABPersonPhoneProperty));
CFStringRef firstName, lastName;
firstName = ABRecordCopyValue(ref, kABPersonFirstNameProperty);
lastName = ABRecordCopyValue(ref, kABPersonLastNameProperty);
[dOfPerson setObject:[NSString stringWithFormat:#"%# %#", firstName, lastName] forKey:#"name"];
//For Email ids
ABMutableMultiValueRef eMail = ABRecordCopyValue(ref, kABPersonEmailProperty);
if(ABMultiValueGetCount(eMail) > 0) {
[dOfPerson setObject:(__bridge NSString *)ABMultiValueCopyValueAtIndex(eMail, 0) forKey:#"email"];
}
NSLog(#"ABMultiValueGetCount(phones)=%ld",ABMultiValueGetCount(phones));
//For Phone number
NSString* mobileLabel;
for(CFIndex i = 0; i < ABMultiValueGetCount(phones); i++) {
mobileLabel = (__bridge NSString*)ABMultiValueCopyLabelAtIndex(phones, i);
if([mobileLabel isEqualToString:(NSString *)kABPersonPhoneMobileLabel])
{
[dOfPerson setObject:(__bridge NSString*)ABMultiValueCopyValueAtIndex(phones, i) forKey:#"Phone"];
}
else if ([mobileLabel isEqualToString:(NSString*)kABPersonPhoneIPhoneLabel])
{
[dOfPerson setObject:(__bridge NSString*)ABMultiValueCopyValueAtIndex(phones, i) forKey:#"Phone"];
break ;
}
}
[contactList addObject:dOfPerson];
}
return contactList;
}
There's a beautiful port of Google's Phone number handling library libPhoneNumber for iOS.
It can help you distinguish between fixed line, mobile phone, toll-free, premium numbers and much more. You can add this library to your project using Cocoapods and follow the README for documentation.
Here's the link (iOS Port): https://github.com/iziz/libPhoneNumber-iOS
Google libphonenumber library (For Android): https://github.com/googlei18n/libphonenumber
Both of them carry almost the same functionality and are very accurate in detecting whether the number is valid or not. It saved us a lot of time and money whenever we encountered a phone number incapable of receiving SMS such as that of a landline.
However, as mentioned in the comments as well, most of the fixed line or landline numbers are capable of receiving SMS, in that case, you should ask the user whether they want to have an SMS on specified number or not.
Thank You
Fennec
Happy Coding!

Ios get contact Details from contact number

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

fetching phone number from address book

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;

Contact numbers iphone sdk

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);
}
}

Strange behavior of iOS simulator and real device(contacts+nsdictionary)

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.

Resources