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;
}
Related
There are lots of applications doing AddressBook synchronization with their Backend servers to cross check which contacts in your AddressBook are using their application and which users needs to be invited to their application.
For the first time it may do a full sync, but after that it shouldn't be a full sync.
My first question is, What is the best way to sync the full AddressBook with a Backend Server?
Second question is, How to sync ONLY the contacts which has modified recently?
If there's any sample application or a tutorial please share with me.
Thanks in advance.
Hello i am also working on same concept.
First of all i don’t have any tutorial to share with you. But what i am doing in application i am sharing with you entire flow.
When application runs first time i am fetching all the contact numbers and sending to server.
But when any contact modifies i am sending modified contact number only.
You will get a call back from address book when contact is modified. There will be specific record id for each contact
Swift
typealias ABExternalChangeCallback = CFunctionPointer<((ABAddressBook!, CFDictionary!,UnsafeMutablePointer) -> Void)>
From this property you will come to know when your contact was modified recently.
ABRecordCopyValue(record, kABPersonModificationDateProperty).takeRetainedValue() as? NSDate
So either way you can check for contact numbers which were modified recently and then again just send those contact to server.
I hope it may helps you!.
Thanks
Get All Phone number from address book as formatted string and post this string to web-server. this make easy to sent all contact at small delay. at web-server side you can code to split phone number to separate number and check for user with same number.
-(NSString *)getPhoneContactAsSingleString
{
NSString *numberString=[NSString new];
numberString=#"";
CFErrorRef *error = nil;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
__block BOOL accessGranted = NO;
if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6
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 { // we're on iOS 5 or older
accessGranted = YES;
}
if (accessGranted) {
#ifdef DEBUG
//NSLog(#"Fetching contact");
#endif
NSMutableArray *contacts=[[NSMutableArray alloc]init];
NSMutableDictionary *numberNameDictionary=[[NSMutableDictionary alloc]init];
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = CFArrayGetCount(allPeople);
for (int i = 0; i < nPeople; i++)
{
NSMutableDictionary *dictionary=[[NSMutableDictionary alloc]init];
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
if (person) {
NSString *firstname=[NSString new];
NSString *lastname =[NSString new];
CFStringRef firstnameStringRef=ABRecordCopyValue(person, kABPersonFirstNameProperty);
CFStringRef lastnameStringRef=ABRecordCopyValue(person, kABPersonLastNameProperty);
if (firstnameStringRef) {
firstname=[NSString stringWithFormat:#"%#",firstnameStringRef];
}
if (lastnameStringRef) {
lastname =[NSString stringWithFormat:#"%#",lastnameStringRef];
}
NSString *name=[NSString new];
if (firstname.length!=0)
{
name=firstname;
if (lastname.length!=0) {
name=[name stringByAppendingString:#" "];
name=[name stringByAppendingString:lastname];
}
}
NSMutableArray *phoneNumbers = [[NSMutableArray alloc] init];
ABMultiValueRef multiPhones = ABRecordCopyValue(person, kABPersonPhoneProperty);
for(CFIndex i=0;i<ABMultiValueGetCount(multiPhones);i++) {
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(multiPhones, i);
NSString *phoneNumber = (__bridge NSString *) phoneNumberRef;
NSString *braketStripedNum = [phoneNumber stringByReplacingOccurrencesOfString:#"(" withString:#""];
braketStripedNum = [braketStripedNum stringByReplacingOccurrencesOfString:#")" withString:#""];
phoneNumber=braketStripedNum;
NSString *dashStripedNum = [self clean_phonenumber:phoneNumber with_string:#"-"];
phoneNumber=dashStripedNum;
NSString *comaStripedNum = [self clean_phonenumber:phoneNumber with_string:#","];
phoneNumber=comaStripedNum;
NSString *dotStripedNum = [self clean_phonenumber:phoneNumber with_string:#"."];
phoneNumber=dotStripedNum;
NSString *spaceStripedNum = [self clean_phonenumber:phoneNumber with_string:#" "];
phoneNumber=spaceStripedNum;
if (phoneNumber.length>10) {
phoneNumber=[phoneNumber substringFromIndex:phoneNumber.length-10];
}
if(phoneNumber.length!=0){
[phoneNumbers addObject:phoneNumber];
[numberNameDictionary setObject:name forKey:phoneNumber];
//NSLog(#"[%#] --> [%#]",orginal_phonenumber,phoneNumber);
numberString=[NSString stringWithFormat:#"%#%#,",numberString,phoneNumber];
}
}
//NSLog(#"%#",phoneNumbers);
if (phoneNumbers.count!=0) {
[dictionary setObject:name forKey:#"name"];
[dictionary setObject:phoneNumbers forKey:#"phonenumbers"];
[contacts addObject:dictionary];
}
}
}
if (numberString.length!=0) {
numberString=[numberString substringToIndex:[numberString length]-1];
}
}
else
{
#ifdef DEBUG
NSLog(#"Cannot fetch Contacts :( ");
#endif
}
return numberString;
}
Clean phone numbers
-(NSString *)clean_phonenumber:(NSString *)number with_string:(NSString *)string
{
NSArray *numberStrips = [number componentsSeparatedByString:string];
NSString *cleanNumber = #"";
for (NSString *eachString in numberStrips) {
cleanNumber = [NSString stringWithFormat:#"%#%#", cleanNumber, eachString];
}
return cleanNumber;
}
Just Check the method
NSLog(#"%#",[self getPhoneContactAsSingleString]);
NSArray *allContacts = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBookRef);
BOOL isExisted=FALSE;
for (id record in allContacts){
ABRecordRef thisContact = (__bridge ABRecordRef)record;
if(thisContact!=nil){
if (CFStringCompare(ABRecordCopyCompositeName(thisContact),
ABRecordCopyCompositeName(newPerson), 0) == kCFCompareEqualTo){
//The contact already exists!
isExisted=YES;
}
}
}
Above is my code to get the contacts and check if that contact is already exist in Addressbook but its getting an error at line if
(CFStringCompare(ABRecordCopyCompositeName(thisContact),
ABRecordCopyCompositeName(newPerson), 0) == kCFCompareEqualTo)
can anyone please help me..
am using ARC Enabled Project
Hi I had the same issue the problem is to check if your current new person hasn't any nil fields and before making comparison check if thisContact name field is not nil.
This is how i did:
NSString *firstName = CFBridgingRelease(ABRecordCopyValue(thisContact, kABPersonFirstNameProperty));
NSString *lastName = CFBridgingRelease(ABRecordCopyValue(thisContact, kABPersonLastNameProperty));
you can check the phone number, email if you want to make a comparison
I'm trying to get a shipping address extracted from the ABRecordRef provided by Apple. I have the following but my street is always returning as nil:
ABMultiValueRef addresses = ABRecordCopyValue(abRecordRef, kABPersonAddressProperty);
for (CFIndex index = 0; index < ABMultiValueGetCount(addresses); index++)
{
CFDictionaryRef properties = ABMultiValueCopyValueAtIndex(addresses, index);
NSString *street = [(__bridge NSString *)(CFDictionaryGetValue(properties, kABPersonAddressStreetKey)) copy];
NSLog(#"street: %#", street);
}
What am I doing wrong?
Even when debugging with the following:
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didSelectShippingAddress:(ABRecordRef)customShippingAddress
completion:(void (^)(PKPaymentAuthorizationStatus status, NSArray *methods, NSArray *items))completion
{
NSLog(#"%#", ABRecordCopyValue(customShippingAddress, kABPersonAddressProperty);
completion(PKPaymentAuthorizationStatusSuccess, ..., ...);
}
I get this with no street:
ABMultiValueRef 0x17227fbc0 with 1 value(s)
0: Shipping (0x17227fa00) - {
City = "Marina del Rey";
Country = "United States";
State = California;
ZIP = 90292;
} (0x172447440)
Edit:
I'm also experiencing issues with accessing names and phone attributes:
NSString *name = (__bridge_transfer NSString *)(ABRecordCopyCompositeName(abRecordRef));
NSString *fname = (__bridge_transfer NSString *)ABRecordCopyValue(abRecordRef, kABPersonFirstNameProperty);
NSString *lname = (__bridge_transfer NSString *)ABRecordCopyValue(abRecordRef, kABPersonFirstNameProperty);
if (!name && fname && lname) name = [NSString stringWithFormat:#"%# %#", fname, lname];
NSLog(#"name: %#", name); // nil
This is how the PKPaymentRequest is being created:
PKPaymentRequest *pr = [[PKPaymentRequest alloc] init];
[pr setMerchantIdentifier:#"********"];
[pr setCountryCode:#"US"];
[pr setCurrencyCode:#"USD"];
[pr setMerchantCapabilities:PKMerchantCapability3DS];
[pr setSupportedNetworks:#[PKPaymentNetworkAmex, PKPaymentNetworkVisa, PKPaymentNetworkMasterCard]];
[pr setPaymentSummaryItems:[self paymentSummaryItems]];
[pr setRequiredBillingAddressFields:PKAddressFieldAll];
[pr setRequiredShippingAddressFields:PKAddressFieldAll];
[pr setShippingMethods:[self supportedShippingMethods]];
Turns out Apple's docs on this weren't that great but the issue is that in the delegate callback for paymentAuthorizationViewController:didSelectShippingAddress:completion: a partial address is always returned. The fix is to also set it in the callback from:
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didAuthorizePayment:(PKPayment *)payment
completion:(void (^)(PKPaymentAuthorizationStatus))completion
{
// Use this instead.
[payment shippingAddress];
}
I also removed a call to setting the required billing addresses (maybe a separate bug).
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.
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.