I need to retrieve list of Contacts in iOS.
Here is my code that is not work.
NSMutableArray *myContacts = [[NSMutableArray alloc]init];
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
if (addressBook!=nil)
{
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);
person.firstName = firstName;
[myContacts addObject:person];
}
CFRelease(addressBook);
}
else
{
NSLog(#"Error");
}
How can i get the list of Contacts?
You need to request access to the user's address book first. Set a flag for checking whether the user allowed/denied access.
__block BOOL userDidGrantAddressBookAccess;
CFErrorRef addressBookError = NULL;
if ( ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined ||
ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized )
{
addressBook = ABAddressBookCreateWithOptions(NULL, &addressBookError);
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error){
userDidGrantAddressBookAccess = granted;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
else
{
if ( ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusDenied ||
ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusRestricted )
{
// Display an error.
}
}
Then you can call that method you wrote to grab the contacts. Remember to check the value of userDidGrantAddressBookAccess first.
Related
I've caught an error:
Fatal Exception: NSRangeException
*** -[__NSArrayI objectAtIndex:]: index 1330 beyond bounds [0 .. 1329]
At string
NSArray *currentABRecords = [ApplicationPhoneDirectory records];
ApplicationPhoneDirectory is NSObject with unique contacts (dict and array).
But I think the error is about working with memory, how can I manage it more properly to avoid appearance of such a problem?
Or, in case if I'm incorrect I can put records part in there.
upd:
static NSArray *RecordsForABInterval(UInt32 start, UInt32 end, CFArrayRef allPeople) {
NSMutableArray *peoples = [NSMutableArray arrayWithCapacity:(end - start) * 2];
for (int i = (int) start; i < end; i++) {
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
NSString *firstName = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
NSString *fullName = [NSString stringWithFormat:#"%# %#",
firstName.length > 0 ? firstName : #"",
lastName.length > 0 ? lastName : #""];
fullName = [fullName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
ABMutableMultiValueRef phoneList = ABRecordCopyValue(person, kABPersonPhoneProperty);
CFIndex phonesCount = ABMultiValueGetCount(phoneList);
for (int j = 0; j < phonesCount; ++j) {
CFTypeRef ABphone = ABMultiValueCopyValueAtIndex(phoneList, j);
NSString *phone = (NSString *)CFBridgingRelease(ABphone);
NSString *phoneInSMFormat = [phone phoneNumberInSMFormat];
if ([phoneInSMFormat isValidSMPhoneNumber]) {
NSDictionary *record = #{
#"name" : fullName,
#"phone" : phoneInSMFormat,
#"original" : phone
};
[peoples addObject:record];
}
}
if (phoneList != NULL) {
CFRelease(phoneList);
}
}
return peoples;
}
+ (NSArray *)records {
const int MaxAutoreleaseStep = 500;
NSMutableArray *records = [NSMutableArray array];
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
__block BOOL accessGranted = NO;
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
dispatch_async(dispatch_get_main_queue(), ^{
accessGranted = granted;
});
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
accessGranted = YES;
}
else {
// The user has previously denied access
// Send an alert telling user to change privacy setting in settings app
if (addressBook != NULL) {
CFRelease(addressBook);
}
return nil;
}
// TODO: review this logic - should it be a single call to AB?
if (addressBook != NULL) {
CFRelease(addressBook);
}
if (accessGranted) {
ABAddressBookRef addressbook = ABAddressBookCreateWithOptions(NULL, &error);
if (error != NULL) {
if (addressbook != NULL) {
CFRelease(addressbook);
}
return nil;
}
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressbook);
CFIndex peopleCount = ABAddressBookGetPersonCount(addressbook);
for (int start = 0; start < peopleCount; start += MaxAutoreleaseStep) {
#autoreleasepool {
int end = start + MaxAutoreleaseStep;
NSArray *contactListCut = RecordsForABInterval((UInt32) start, (UInt32) MIN((int)peopleCount, end), allPeople);
[records addObjectsFromArray:contactListCut];
}
}
if (allPeople != NULL) {
CFRelease(allPeople);
}
if (addressbook != NULL) {
CFRelease(addressbook);
}
}
return records;
}
upd2:
- (NSString *)phoneNumberInSMFormat {
NSString *phone = [self numericPresentation];
if (phone.length > 0 && [phone characterAtIndex:0] == '8') {
NSMutableString *modifiedString = [phone mutableCopy];
[modifiedString replaceCharactersInRange:NSMakeRange(0, 1) withString:#"7"];
phone = modifiedString;
}
return phone;
}
- (BOOL)isValidSMPhoneNumber {
static NSCharacterSet *characterSet = nil;
if (characterSet == nil) {
characterSet = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
}
if ([self rangeOfCharacterFromSet:characterSet].location != NSNotFound) {
return NO;
}
if (self.length < 7 || self.length > 12) {
return NO;
}
return [self characterAtIndex:0] != '8';
}
I am new in IOS Programming. In my simple app I am storing first name,last name and phone number in NSMutableArray.I have a UITextfield where I get the phone number.I wanna search first name and last name according to mobile number.I am storing firstname, lastname and phone number in different Mutable arrays so how to search firstname and lastname in stored array.
This is my code
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
_firstname = [[NSMutableArray alloc]init];
_lastname = [[NSMutableArray alloc]init];
_number = [[NSMutableArray alloc]init];
_fullname = [[NSMutableArray alloc]init];
_arrContacts = [[NSMutableArray alloc]init];
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
ABAddressBookRef addressBook = ABAddressBookCreate( );
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
CFErrorRef *error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);
for(int i = 0; i < numberOfPeople; i++) {
ABRecordRef person = CFArrayGetValueAtIndex( allPeople, i );
NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
if (firstName != nil) {
[_firstname addObject:firstName];
}
else{
[_firstname addObject:#"Not Found"];
}
if (lastName != nil) {
[_lastname addObject:lastName];
}
else{
[_lastname addObject:#"Not Found"];
}
ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
[[UIDevice currentDevice] name];
NSArray *numbers = (__bridge NSArray *)(ABMultiValueCopyValueAtIndex(phoneNumbers, 0));
if (numbers != nil) {
[_arrContacts addObject:numbers];
NSLog(#"Numbers:%#",_arrContacts);
}
for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
NSString *phoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
_addressBookNum = [_addressBookNum stringByAppendingFormat: #":%#",phoneNumber];
}
}
// NSLog(#"AllNumber:%#",_addressBookNum);
}
else {
// Send an alert telling user to change privacy setting in settings app
}
CFRelease(addressBookRef);
NSLog(#"This is Name: %#",_firstname);
NSLog(#"This is LastName: %#",_lastname);
NSLog(#"Number: %#",_arrContacts);
}
Please suggest some idea to do this
Instead of saving name last name and number in different array store it in single array with dictionary. Suppose NSMutabelArray *details
create dictionary having three values name lastname and number, and add this dictoianry into array,
so it will be easy for you to further use
I'm trying to NSLog name and number from my contact in Address Book.
I succeed to log the name, but I don't succeed to solve the problem with number.
Here is my code :
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
if (addressBook != NULL) {
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (granted) {
CFArrayRef allNames = ABAddressBookCopyArrayOfAllPeople(addressBook);
if (allNames != NULL) {
NSMutableArray *names = [NSMutableArray array];
for (int i = 0; i < CFArrayGetCount(allNames); i++) {
ABRecordRef group = CFArrayGetValueAtIndex(allNames, i);
CFStringRef name = ABRecordCopyCompositeName(group);
[names addObject:(__bridge NSString *)name];
CFRelease(name);
}
NSLog(#"names = %#", names);
CFRelease(allNames);
}
}
CFRelease(addressBook);
});
}
Maybe I have to create NSDictionnary ? I don't know how to solve it...
The phone numbers are a ABMultiValueRef:
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
if (phones != NULL) {
for (NSInteger index = 0; index < ABMultiValueGetCount(phones); index++) {
NSString *phone = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phones, index));
NSString *label = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(phones, index)); // either kABHomeLabel or kABPersonPhoneMainLabel or ...
// do something with `phone` and `label`
}
CFRelease(phones);
}
You'll need to use ABRecordCopyValue(group, kABPersonPhoneProperty) which returns ABMultiValueRef. See https://stackoverflow.com/a/286281/171089 for more.
I am trying to get contacts list like this:
CFErrorRef *error = nil;
ABAddressBookRef addressBook = nil;
__block BOOL accessGranted = NO;
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined ||
ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
addressBook = ABAddressBookCreateWithOptions(NULL, error);
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
accessGranted = granted;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusDenied ||
ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusRestricted) {
return nil;
}
if (!accessGranted) {
if (addressBook) CFRelease(addressBook);
return nil;
}
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
if (nPeople <= 0) {
CFRelease(addressBook);
return nil;
}
ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByFirstName);
if (!allPeople) return nil;
NSMutableArray *contactsArray = [NSMutableArray arrayWithCapacity:nPeople];
for (CFIndex i = 0; i < nPeople; ++i) {
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
if (!person) continue;
ContactData *contact = [ContactData new];
contact.firstName = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
It is working on my iPhone 5s and simulator, but the build is crashing on the tester iPod device on the line with SIGSEGV:
contact.firstName = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
Here is the stack:
3 AppSupport 0x34129a04 CPRecordCopyProperty
4 AppSupport 0x34129a04 CPRecordCopyProperty
5 AddressBook 0x2fd6ad22 ABRecordCopyValueUnfiltered
6 AddressBook 0x2fd6abc6 ABRecordCopyValue
I had the same error, the problem is that:
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByFirstName);
and
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
are giving a different number of contacts (so in your case nPeople is probably bigger than allPeople, which causes the crash). "source" doesn't seem to be giving all the contacts in the address book. Changing it to nil solved it for me. Also, to be sure I would do: nPeople = CFArrayGetCount(allPeople);
The solution is very well explained by Jokinryou Tsui in this post: ABAddressBookCopyArrayOfAllPeople and ABAddressBookGetPersonCount return different sizes
(This is my first post, so I'm not sure if I broke any rules or followed the right procedure. I hope the answer helps!)
In my app I have to access the address book, so in iOS>6.0 I have to ask permission to the user.
I do this:
ABAddressBookRef addressBook = ABAddressBookCreate();
if(floor(NSFoundationVersionNumber) >= NSFoundationVersionNumber_iOS_6_0)
{
//iOS is >= 6.0
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
{
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error)
{
if (granted)
{
[self showContacts:addressBook];
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
{
[self showContacts:addressBook];
}
else
{
//showAccessDeniedAlert();
}
}
else
{
//iOS is < 6.0
[self showContacts:addressBook];
}
In showContacts it's all alright, but: the first time I use the app on a device, it asks me if I want to let it access the AddressBook, i press "OK" and showContactsisn't called! So I have to close the app, restart it, and it works perfectly.
This is my showContacts method:
- (void) showContacts:(ABAddressBookRef)addressBook
{
CFArrayRef allContacts = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfContacts = ABAddressBookGetPersonCount(addressBook);
for (int ii = 0; ii < numberOfContacts; ii++)
{
ABRecordRef contactRef = CFArrayGetValueAtIndex(allContacts, ii);
ABMultiValueRef *phones = ABRecordCopyValue(contactRef, kABPersonPhoneProperty);
for(CFIndex jj = 0; jj < ABMultiValueGetCount(phones); jj++)
{
CEPerson *person = [[CEPerson alloc] init];
NSString *name = (__bridge_transfer NSString *)ABRecordCopyValue(contactRef,kABPersonFirstNameProperty);
NSString *surname = (__bridge_transfer NSString *)ABRecordCopyValue(contactRef, kABPersonLastNameProperty);
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(phones, jj);
NSString *phoneNumber = (__bridge NSString *)phoneNumberRef;
person.name = name;
person.surname = surname;
person.telephone = phoneNumber;
[arrContacts addObject:person];
}
}
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"surname" ascending:YES];
[arrContacts sortUsingDescriptors:[NSArray arrayWithObject:sort]];
[self selectAllContacts];
}
How can I make it work even right after the user presses "OK"?
Thank you all.
I figured it out. I simply added
[tableView reloadData];
at the end of my showContacts method.