Question regarding errand on UITableView creation from iPhone Addressbook - uitableview

I have a question regarding the AddressBook Framework for iOS. The situation is as follows:
I'm trying recreate the contacts view from the phone application, but I want to show the contact's phone numbers in the same view. So if a contact has more than one number, his name will be in the TableView multiple times, each time with a different number.
I am trying to accomplish that by extracting all the information I need when the view loads and after that, populate the TableView with the appropriate values from an NSArray consisting of NSDictionaries containing the contact's information.
This works great except for one thing... The contact's phonenumbers and labels are read correctly and stored in the dictionary, but when I read them out later, they seem to have vanished.
Here's my code for generating the NSDictionaries, I bet it's some kind of memory management error or something completly stupid. I hope anyone can help me, thanks a lot in advance!
persons = [[NSMutableArray alloc] init];
ABAddressBookRef addressBook = ABAddressBookCreate();
ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
NSArray *people = (NSArray *)ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByLastName);
for (id record in people)
{
ABMultiValueRef numbers = ABRecordCopyValue((ABRecordRef)record, kABPersonPhoneProperty);
for (CFIndex i = 0; i < ABMultiValueGetCount(numbers); ++i) {
CFStringRef label = ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(numbers, i));
CFStringRef number = ABMultiValueCopyValueAtIndex(numbers, i);
CFStringRef firstNameRef = ABRecordCopyValue((ABRecordRef)record, kABPersonFirstNameProperty);
CFStringRef lastNameRef = ABRecordCopyValue((ABRecordRef)record, kABPersonLastNameProperty);
CFDataRef imageDataRef = ABPersonCopyImageDataWithFormat((ABRecordRef)record, kABPersonImageFormatThumbnail);
NSString *firstName = [NSString stringWithFormat:#"%#", firstNameRef];
NSString *lastName = [NSString stringWithFormat:#"%#", lastNameRef];
NSString *pLabel = [[NSString alloc] initWithFormat:#"%#", label];
NSString *pNumber = [NSString stringWithFormat:#"%#", number];
UIImage *image = [UIImage imageWithData:(NSData*)imageDataRef];
NSDictionary *personDict = [NSDictionary dictionaryWithObjectsAndKeys:firstName, #"firstName", lastName, #"lastName", image, #"image", pNumber, #"phoneNumber", pLabel, #"label", nil];
NSLog(#"In %# - %#", pLabel, pNumber);
[persons addObject:personDict];
CFRelease(firstNameRef);
CFRelease(lastNameRef);
//CFRelease(imageDataRef);
CFRelease(label);
CFRelease(number);
}
}
CFRelease(addressBook);
CFRelease(source);
[people release];

Finally could resolve this myself. Apparently I was adding some nil-images to the dictionaries which they couldn't handle.

Related

stringByReplacingOccurrencesOfString only works on some objects (NSString black in debugger instead of blue)

I have a method for getting all the contacts from the iPhone contacts app and i want to add the phone numbers to an object after i have removed all the spaces in the phone number string. The problem is that this only works for some of the contacts. I have noticed that in the debugger the string-objects sometimes show in a blue color and sometimes in black. Anybody have a clue what is going on here?
Images:
Does not remove spaces in phone number
http://ctrlv.in/293692
Removes spaces in phone number
http://ctrlv.in/293691
Code:
ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
CFArrayRef sortedPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByFirstName);
//RETRIEVING THE FIRST NAME AND PHONE NUMBER FROM THE ADDRESS BOOK
CFIndex number = CFArrayGetCount(sortedPeople);
NSString *firstName;
NSString *phoneNumberFromContact;
for(int i = 0; i < number; i++)
{
ABRecordRef person = CFArrayGetValueAtIndex(sortedPeople, i);
firstName = (__bridge NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
phoneNumberFromContact = (__bridge NSString *) ABMultiValueCopyValueAtIndex(phones, 0);
if(phoneNumberFromContact != NULL)
{
Contact *contact = [[Contact alloc]init];
contact.firstName = firstName;
phoneNumberFromContact = [phoneNumberFromContact stringByReplacingOccurrencesOfString:#" " withString:#""];
contact.phoneNumber = phoneNumberFromContact;
[self.contacts addObject:contact];
}
}
There are many characters that appear as spaces in NSStrings. Removing all instances of all of them is rather difficult. The best method for your case is to keep only the characters you want (numbers). As the answer you referenced states you need to create a set of characters to keep:
NSCharacterSet *numbers = [NSCharacterSet
characterSetWithCharactersInString:#"0123456789"];
Then you need to remove everything but those characters. This can be done in a few different ways. The scanner in the answer you suggested is likely the fastest. But the way with the fewest lines of code (and in my mind most readable) would be something like:
number = [[number componentsSeparatedByCharactersInSet: numbers] componentsJoinedByString: #""];
It may be slow, so if you do this a million times then keep to the scanner.

Copy contacts phone numbers to array using ABAdressPhoneBookRef

I think I need to use ABMultiValueCopyArrayOfAllValues to grab all phone numbers from my ABAddressBookRef reference variable.
(If this is not the correct way to do it, please give me a correct way; ANY way that gives me access to contact phone numbers will do)
What I'd like to do now, is grab my user's contact's phone numbers(preferably their cell) and add that to an array.
How do I grab just the numbers and add that to an array?
Any help, suggestions, or advice with this is greatly appreciated in advice, I'm not finding this anywhere.
Thanks.
If I were you, I would use this approach:
NSMutableArray *allPhoneNumbers = #[].mutableCopy;
NSArray *allContact = (__bridge NSArray*)ABAddressBookCopyArrayOfAllPeople(book);
for (id rec in allContacts){
ABMultiValueRef mvr = ABRecordCopyValue((__bridge ABRecordRef)rec, kABPersonPhoneProperty);
NSArray *currentNums = (__bridge NSArray*) ABMultiValueCopyArrayOfAllValues(mvr);
[allPhoneNumbers addObjectsFromArray: currentNums];
}
I have not tested this, but it should work. Tell me if you have any issues.
If you want just one contact's numbers (the above gets every contact), use this code.
ABMultiValueRef mvr = ABRecordCopyValue(yourRecordRef, kABPersonPhoneProperty);
NSArray *currentNums = (__bridge NSArray*) ABMultiValueCopyArrayOfAllValues(mvr);
[allPhoneNumbers addObjectsFromArray: currentNums];
ABMultiValueRef phoneNumbers = ABRecordCopyValue(ref, kABPersonPhoneProperty);
for(CFIndex j = 0; j < ABMultiValueGetCount(phoneNumbers); j++)
{
CFStringRef phoneNumberRefCF = ABMultiValueCopyValueAtIndex(phoneNumbers, j);
CFStringRef locLabelCF = ABMultiValueCopyLabelAtIndex(phoneNumbers, j);
NSString *phoneLabelCF =(NSString*) ABAddressBookCopyLocalizedLabel(locLabelCF);
NSString *phoneNumberCF = (NSString *)phoneNumberRefCF;
CFRelease(phoneNumberRefCF);
CFRelease(locLabelCF);
NSLog(#" - %# (%#)", phoneNumberCF, phoneLabelCF);
}

Error getting information from address book

I'm having an issue retrieving an array of certain data from the address book. Here's the code:
- (void) getContacts{
ABAddressBookRef ab = ABAddressBookCreate();
NSMutableArray *retVal = (__bridge NSMutableArray *)(ABAddressBookCopyArrayOfAllPeople(ab));
CFRelease(ab);
contact* temp=[[contact alloc] init];
NSMutableArray* tempArray=[[NSMutableArray alloc]init];
for(int i=0;i<[retVal count];i++)
[tempArray insertObject:
[NSString stringWithFormat:#"%#",
[temp set2:[NSString stringWithFormat:#"%#",
[[retVal objectAtIndex:i] kABPersonFirstNameProperty]]
last:[NSString stringWithFormat:#"%#",
[[retVal objectAtIndex:i] kABPersonLastNameProperty]]
number:[NSString stringWithFormat:#"%#",
[[retVal objectAtIndex:i] kABPersonPhoneProperty]]]] atIndex:i];
_objects=tempArray;
[self alert:[NSString stringWithFormat:#"%#",_objects] title:#"TEMP"];
}
The errors I'm getting are about the kABPerson properties.
Some more clarification: essentially I'm grabbing all the data from the address book into the first array and then I'm manually going through that array and attempting to retrieve the data that I need for the rest of my app.
Any ideas?
Just for more clarification here's my contact.h file:
#interface contact : NSString{
NSString* first;
NSString* last;
NSString* number;
}
#end
And here's my contact.m file:
#implementation contact
- (void) set:(NSString*)first2 last:(NSString*)last2 number:(NSString*)number2{
first=first2;
last=last2;
number=number2;
}
- (contact*) set2:(NSString*)first2 last:(NSString*)last2 number:(NSString*)number2{
first=first2;
last=last2;
number=number2;
return self;
}
#end
Here's that line that seems to be too long to post:
//Enter contact into tempArray
[tempArray insertObject:[NSString stringWithFormat:#"%#",[temp set:[NSString stringWithFormat:#"%#",(__bridge NSString *)(ABRecordCopyValue((__bridge ABMultiValueRef)[retVal objectAtIndex:i],kABPersonFirstNameProperty))] last:[NSString stringWithFormat:#"%#",(__bridge NSString *)(ABRecordCopyValue((__bridge ABMultiValueRef)[retVal objectAtIndex:i],kABPersonLastNameProperty))] number:(__bridge NSString *)ABMultiValueCopyValueAtIndex((__bridge ABMultiValueRef)((__bridge NSString*)ABRecordCopyValue((__bridge ABRecordRef)([retVal objectAtIndex:i]),kABPersonPhoneProperty)), 0)]] atIndex:i];
kABPersonFirstNameProperty is a property ID, not an Objective-C object property.
That is, you can't use [[retVal objectAtIndex:i] kABPersonFirstNameProperty] -- you will instead need to access the first and last names like this:
CFStringRef firstName = ABRecordCopyValue ([retVal objectAtIndex:i], kABPersonFirstNameProperty);
CFStringRef lastName = ABRecordCopyValue ([retVal objectAtIndex:i], kABPersonLastNameProperty);
CFStringRef phoneNum = ABRecordCopyValue ([retVal objectAtIndex:i], kABPersonPhoneProperty);
Then don't forget that ABRecordCopyValue follows the Create Rule -- you'll need to CFRelease(firstName) afterwards.

Contact number stays nil

I have an mutableArray and put the number of a contact in it.
I use the following code:
ABMultiValueRef phoneNumbers = (ABMultiValueRef)ABRecordCopyValue(person, kABPersonPhoneProperty);
CFRelease(phoneNumbers);
number = (__bridge NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
[thenumbers addObject:number];
In order to retrieve it, I do this:
Contacts *num = [thenumbers objectAtIndex:indexPath.row];
NSString *numbers = [NSString stringWithFormat:#"%#", [num number]];
cell.detailTextLabel.text = numbers;
I set breakpoints and it stops at the right lines. I also tried
NSLog(#"%#", number);
And it returns the numbers. And yes I have reloadDate in viewWillappear.
You're releasing phoneNumbers straight after copying to it. Try moving it to after you use it.
Or, better still, transfer the ownership to the NSString object through ARC
ABMultiValueRef phoneNumbers = (ABMultiValueRef)ABRecordCopyValue(person, kABPersonPhoneProperty);
number = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
And there is no need to call CFRelease(phoneNumbers) at all.
As for getting the numbers into the cell, you're getting confused about your types. You're putting an NSString into theNumber but your pulling out a Contacts. And then sending it some kind of number message.
You've put a string in the array, you can only pull out a string or a subclass.
NSString *numbers = [thenumbers objectAtIndex:indexPath.row];
cell.detailTextLabel.text = numbers;
In the following code,
ABMultiValueRef phoneNumbers = (ABMultiValueRef)ABRecordCopyValue(person, kABPersonPhoneProperty);
CFRelease(phoneNumbers);
number = (__bridge NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
You are releasing the phoneNumbers and then you are accesing the variable. Change it to as follows.
ABMultiValueRef phoneNumbers = (ABMultiValueRef)ABRecordCopyValue(person, kABPersonPhoneProperty);
number = (__bridge NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
CFRelease(phoneNumbers);

How to use ABUnknownPersonViewController with generated data entered intially?

This is very specific case. I believe someone had already solved this somewhere, but it's not easy for me to find it.
The situation:
1 ) an object will return NSString objects for name address1, address2, phone:
[anObject name];
[anObject address1];
[anObject address2];
[anObject name];
2 ) I would like to use these objects to prepare ABUnknownPersonViewController with initially entered values, so the user will not have to enter them before saving them in Address Book.
I have looked at iOS documents and searched through Google and StackOverflow, can't find the right answer for this simple situation.
Can anyone guide me on this?
Found an answer: It's nicely documented in iOS Developer Library:
http://developer.apple.com/library/ios/#samplecode/QuickContacts/Listings/Classes_QuickContactsViewController_m.html#//apple_ref/doc/uid/DTS40009475-Classes_QuickContactsViewController_m-DontLinkElementID_6
Here is a sample code I implemented to return a ABPersonRecordRef as an object. The error I had experienced was related to not retaining the ABPersonRecordRef object after returning it.
- (id)personRecordUsingModelObj:(id)modelObj {
ABRecordRef aContact = ABPersonCreate();
CFErrorRef anError = NULL;
NSString *name = [NSString stringWithFormat:#"%#", [modelObj name]];
ABRecordSetValue(aContact, kABPersonOrganizationProperty, name, &anError);
ABMultiValueRef phone = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(phone, [modelObj phone], kABPersonPhoneMainLabel, NULL);
ABRecordSetValue(aContact, kABPersonPhoneProperty, phone, &anError);
CFRelease(phone);
NSString *address = [NSString stringWithFormat:#"%# %#", [modelObj addr1], [modelObj addr2]];
NSMutableDictionary *dictionaryAddress = [[NSMutableDictionary alloc] initWithCapacity:0];
[dictionaryAddress setObject:address forKey:(NSString *)kABPersonAddressStreetKey];
[dictionaryAddress setObject:#"us" forKey:(NSString *)kABPersonAddressCountryCodeKey];
ABMutableMultiValueRef address = ABMultiValueCreateMutable(kABDictionaryPropertyType);
ABMultiValueAddValueAndLabel(address, dictionaryAddress, kABPersonAddressStreetKey, NULL);
[dictionaryAddress release];
ABRecordSetValue(aContact, kABPersonAddressProperty, address, &anError);
CFRelease(address);
if (anError) {
aContact = nil;
}
[(id)aContact autorelease];
return (id)aContact;
}

Resources