I'm getting crash on this line.
phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(numbers, index));
If the first phone number is selected I get index of 1 which is wrong. It should be 0 and therefore choses wrong number. If I select second number it gives index of -1 which crashes the app.
#pragma mark helper methods
- (void)didSelectPerson:(ABRecordRef)person identifier:(ABMultiValueIdentifier)identifier {
NSString *phoneNumber = @"";
ABMultiValueRef numbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
if (numbers) {
if (ABMultiValueGetCount(numbers) > 0) {
CFIndex index = 0;
if (identifier != kABMultiValueInvalidIdentifier) {
index = ABMultiValueGetIndexForIdentifier(numbers, identifier);
}
phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(numbers, index));
}
CFRelease(numbers);
}
self.numberTextField.text = [NSString stringWithFormat:@"%@", phoneNumber];
}
There is a bug in iOS 8.3 (and presumably previous version of iOS 8) when working on copies of contacts that have had phone numbers/emails removed. The documentation for
ABPeoplePickerNavigationControllerstates that:In my testing I had a contact which had three phone numbers (let's call them
111,222and333). It appears thatidentifiers are fixed, stable zero-based values. Thus my three phone numbers were identifier0to2. If a phone number is deleted the identifiers do not change. Zero-basedindexes are used to access the current list of phone numbers (or emails etc.) andABMultiValueGetIndexForIdentifieris used to convert an identifier into an index.In my test I deleted the first phone number,
111. This does not change the identifiers for the remaining phone numbers (222=1,333=2).When I used
ABPeoplePickerNavigationControllerand chose the first phone number (222) the delegate methodpeoplePickerNavigationController: didSelectPerson:property:identifier:correctly passed an identifier of1. However,ABMultiValueGetIndexForIdentifierreturned an index of 1, not 0 and my app then copied the phone number333as the one it thought the user had selected. If the user picked333then I was correctly passed an identifier of2butABMultiValueGetIndexForIdentifierconverted that to-1and then an unprotected call toABMultiValueCopyValueAtIndexcrashed.So, when working on a copy of the contact (which is what happens in iOS 8 when the app has not been authorised to access the address book), iOS seems to be using identifiers based on the real contact, but indexes are based on the copy. The copy seems to have forgotten the previously-deleted phone number and the identifier-to-index mapping goes wrong if the user picks a phone number that was created after a previously-deleted phone number. It works if the user hasn't deleted phone numbers, or if they have deleted phone numbers after the one they pick.
The workaround is to complicate the app by making it ask the user for permission to access the Address Book using
ABAddressBookRequestAccessWithCompletion. Once granted, the app will not be given a copy of the selected contact and the identifier-to-index mapping works correctly.