Loading Apple Pay Shipping Address No Street - ios

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

Related

Sync iPhone AddressBook with a Backend Server

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

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.

IOS: reverse geocodign province

I have this code to reverse geocoding and it work
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark
{
MKPlacemark * myPlacemark = placemark;
// with the placemark you can now retrieve the city name
NSString *region = [myPlacemark.addressDictionary objectForKey:(NSString*) kABPersonAddressStateKey];
NSString *city = [myPlacemark.addressDictionary objectForKey:(NSString*) kABPersonAddressCityKey];
NSString *address = [myPlacemark.addressDictionary objectForKey:(NSString*) kABPersonAddressStreetKey];
NSLog(#"region:%#", region);
NSLog(#"city:%#", city);
NSLog(#"address:%#", address);
}
It work fine but I'm not able to have "province"...what's the way to obtain province?
These are all the keys for the address dictionary:
const ABPropertyID kABPersonAddressProperty;
const CFStringRef kABPersonAddressStreetKey;
const CFStringRef kABPersonAddressCityKey;
const CFStringRef kABPersonAddressStateKey;
const CFStringRef kABPersonAddressZIPKey;
const CFStringRef kABPersonAddressCountryKey;
const CFStringRef kABPersonAddressCountryCodeKey;
Constants
kABPersonAddressProperty Identifier for the address multivalue property. Available in iOS 2.0 and later.
Declared in ABPerson.h.
kABPersonAddressStreetKey Street. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressCityKey City. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressStateKey State. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressZIPKey Zip code. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressCountryKey Country. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressCountryCodeKey Country code. The supported values are listed in “Country Codes.” Available in iOS 2.0 and
later. Declared in ABPerson.h.
Unfortunately there isn't one for province.
In My code, I have following ways to get address and place names.
Province is State or administrativeArea
[geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
if(placemarks.count){
NSDictionary *dictionary = [[placemarks objectAtIndex:0] addressDictionary];
addressLabel.Text = [dictionary valueForKey:#"Street"];
cityLabel.Text = [dictionary valueForKey:#"City"];
stateLabel.Text = [dictionary valueForKey:#"State"];
zipCodeLabel.Text = [dictionary valueForKey:#"ZIP"];
countryLabel.text = [dictionary valueForKey:#"Country"];
countryCodeLabel.text = [dictionary valueForKey:#"CountryCode"];
placeNameLabel.text = [placemarks[0] name];
addressNumberLabel.text = [placemarks[0] subThoroughfare];
addressLabel.text = [placemarks[0] thoroughfare];
neighborhoodLabel.text = [placemarks[0] subLocality];
cityLabel.text = [placemarks[0] locality];
countyLabel.text = [placemarks[0] subAdministrativeArea];
stateLabel.text = [placemarks[0] administrativeArea];
zipCodeLabel.text = [placemarks[0] postalCode];
countryLabel.text = [placemarks[0] country];
countryCodeLabel.text = [placemarks[0] ISOcountryCode];
inlandWaterLabel.text = [placemarks[0] inlandWater];
oceanLabel.text = [placemarks[0] ocean];
}
}];
You can try my open source project. It is easy to get place marks. Device-Details https://github.com/robert-yi-jones/Device-Details

NSLinguisticTagger Memory Leak

I've been fiddling in Xcode 4.2 with iOS 5.0's new NSLinguisticTagger. My objective with this function is to take in an address book record and then spit out a composite name as an NSString, sort of like what ABRecordCopyCompositeName does, but taking into account naming order for East Asian languages and Hungarian (last first instead of first last). Here's the function:
NSString *text = [self getLocalizedFullNameOfRecord:[contacts objectAtIndex:indexPath.section];
- (NSString *) getLocalizedFullNameOfRecord:(ABRecordRef) person
{
NSString *firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *middleName = ABRecordCopyValue(person, kABPersonMiddleNameProperty);
NSString *lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
NSString *prefix = ABRecordCopyValue(person, kABPersonPrefixProperty);
NSString *suffix = ABRecordCopyValue(person, kABPersonSuffixProperty);
NSString *fullName = #"";
__block BOOL Asian;
// Apologies to all Hungarians who aren't actually Asian
__block NSArray *asianLanguages = [NSArray arrayWithObjects:#"zh-Hant", #"zh-Hans", #"ja", #"ko", #"hu", #"vi", nil];
[firstName enumerateLinguisticTagsInRange:NSMakeRange(0, firstName.length) scheme: NSLinguisticTagSchemeLanguage options: NSLinguisticTaggerOmitWhitespace orthography: nil usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop){
if ([asianLanguages containsObject:tag])
Asian = YES;
else
Asian = NO;
}];
if(prefix)
fullName = [fullName stringByAppendingFormat:#"%# ", prefix];
if(Asian && lastName)
fullName = [fullName stringByAppendingFormat:#"%# ", lastName];
else if(firstName)
fullName = [fullName stringByAppendingFormat:#"%# ", firstName];
if(middleName)
fullName = [fullName stringByAppendingFormat:#"%# ", middleName];
if(Asian && firstName)
fullName = [fullName stringByAppendingFormat:#"%# ", firstName];
else if(lastName)
fullName = [fullName stringByAppendingFormat:#"%# ", lastName];
if(suffix)
fullName = [fullName stringByAppendingFormat:#"%#", suffix];
[firstName release];
[middleName release];
[lastName release];
[prefix release];
[suffix release];
return fullName;
}
Instruments tells me that I am leaking some 16 to 32 bytes with every iteration of this function, on the enumerateLinguisticTagger (and not the blocks part, apparently). Since online resources for NSLinguisticTagger is limited to its class reference and a single tutorial, I have no idea where and how to start looking for the leak.
Help please?
I had the same problem. In my case, leaks occurred when string has line breaks (\n or \r).

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