Sync iPhone AddressBook with a Backend Server - ios

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

Related

how to pick the birthday labels from the contacts in Addressbook?

I working with the Addressbook for fetch the contacts list with their labels. (like mobile, main, home, work, fax..etc). I fetched the phone, email labels from the contacts but i did not fetch the birthday, anniversary labels. Here is my code for birthday class.
ABMultiValueRef dateofbirth1 = ABRecordCopyValue(contactPerson, kABPersonBirthdayProperty); // Assign the Date Of birth
NSString *dob1=[NSDateFormatter localizedStringFromDate:(__bridge NSDate *)(dateofbirth1) dateStyle:NSDateFormatterLongStyle timeStyle:0]; // Changing to string format using Date Formatter.
if(!(dob1==nil))
{
// DOB is Not Nill
}
Here is my fetch the BirthDay Label Code
if([arrayOfDatesAsStrings count]>0)
{
for (int j = 0; j < [arrayOfDatesAsStrings count] ; j++)
{
**//This is the fetching birthday label code and following code is crashed**
CFStringRef locLabel1 = ABMultiValueCopyLabelAtIndex((__bridge ABMultiValueRef)(dob1), j);
NSString *phoneLabel1 =(__bridge NSString*) ABAddressBookCopyLocalizedLabel(locLabel1);
personD.dateOfBirth = phone1;
NSLog(#" %# %#",phoneLabel1,personD.dateOfBirth);
}
}
else
{
NSLog(#"Date Of Birth was Not set ");
}
What can i do for this? can Any one help me? The Crash Error Message is :
"Thread 1:Exc_BAD_ACCESS (Code=1, address=0x38)"
To fetch contacts from devices
if (isIOS9) { //checking iOS version of Device
CNContactStore *store = [[CNContactStore alloc] init];
//keys with fetching properties
NSArray *keys = #[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPhoneNumbersKey, CNContactEmailAddressesKey,CNContactPostalAddressesKey, CNLabelWork, CNLabelDateAnniversary];
NSString *containerId = store.defaultContainerIdentifier;
NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
NSError *error;
NSArray *cnContacts = [store unifiedContactsMatchingPredicate:predicate keysToFetch:keys error:&error];
DLOG(#"cnContacts %lu",(unsigned long)cnContacts.count);
if (error) {
//error
} else {
for (CNContact *contact in cnContacts) {
//iterate over cnContacts to get details
}
}
} else {
//for below iOS 9
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef arrPersons = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex count = ABAddressBookGetPersonCount(addressBook);
NSLog(#"cnContacts %lu",(unsigned long)count);
for (int i = 0; i < count; i++) {
ABRecordRef record = CFArrayGetValueAtIndex(arrPersons,i);
//use kABPersonBirthdayProperty to get b’day
NSString *birthDay = (__bridge NSString *)(ABRecordCopyValue(record, kABPersonBirthdayProperty));
NSLog(#“B’day %#”, birthDay);
}
}

find ios ABRecordRef contact in AddressBook by phone, email, name

I need to find a contact in AdreesBook in order to add a new social network.
Sometimes I have to find a contact by its phones and emails or by its phones, firstName and lastName, is there any kind of query to get ABRecordRef contact instead of doing whiles?
Many of my users have more than 1000 contacts and I need to update many of them, so it is not efficient if my only solution is to do so many whiles...
Any idea??
Thanks!
Below is method which may help you to get the Contact details using phone number. For that its used kABPersonPhoneProperty, same way you can write another functions for searching email and name:
For email, use a property : kABPersonEmailProperty
For First Name : kABPersonFirstNameProperty
For more details, go through: ABPerson Reference
Hope this helps.
#import <AddressBook/AddressBook.h>
-(NSArray *)contactsContainingPhoneNumber:(NSString *)phoneNumber {
/*
Returns an array of contacts that contain the phone number
*/
// Remove non numeric characters from the phone number
phoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:#""];
// Create a new address book object with data from the Address Book database
CFErrorRef error = nil;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (!addressBook) {
return [NSArray array];
} else if (error) {
CFRelease(addressBook);
return [NSArray array];
}
// Requests access to address book data from the user
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {});
// Build a predicate that searches for contacts that contain the phone number
NSPredicate *predicate = [NSPredicate predicateWithBlock: ^(id record, NSDictionary *bindings) {
ABMultiValueRef phoneNumbers = ABRecordCopyValue( (__bridge ABRecordRef)record, kABPersonPhoneProperty);
BOOL result = NO;
for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
NSString *contactPhoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
contactPhoneNumber = [[contactPhoneNumber componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:#""];
if ([contactPhoneNumber rangeOfString:phoneNumber].location != NSNotFound) {
result = YES;
break;
}
}
CFRelease(phoneNumbers);
return result;
}];
// Search the users contacts for contacts that contain the phone number
NSArray *allPeople = (NSArray *)CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
NSArray *filteredContacts = [allPeople filteredArrayUsingPredicate:predicate];
CFRelease(addressBook);
return filteredContacts;
}

Loading Apple Pay Shipping Address No Street

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).

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

Question regarding errand on UITableView creation from iPhone Addressbook

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.

Resources