Filter LinkedContacts
We're dealing with this problem http://stackoverflow.com/questions/11351454/dealing-with-duplicate-contacts-due-to-linked-cards-in-ios-address-book-api. Looking at the docs seems there's no way to filter those linked contacts using APAddressBook, is this true? Maybe I missed something?
Hello
I've heard about this problem, APAddressBook even have a pull request that should solve this issue. But I was unable to reproduce this bug and didn't merge it.
Please, provide steps how to create linked contacts that cause duplicates and I'll try to find solution
I'm having the same problem and believe I have steps that you can use to reproduce: 0. Start simulator and Reset Content & Settings to be sure you're in a fresh state.
- Open Contacts app and create two contacts: First Name: First Last Name: User Phone: 314-555-1212 and First Name: Second Last Name: User Phone: 314-555-1212
- Open up First User contact and tap Edit
- Scroll to bottom of edit view and tap "link contacts..."
- Select Second User and tap "Link"
- Tap "Done"
At this point, you can no longer see both "First User" and "Second User" in the list of All Contacts. However, they both get iterated over in the -[APAddressBook loadContactsOnQueue:completion:] completion block.
I suspect the right behavior here is to not to exclude any linked APContact objects (as that pull request does, I believe), but maybe to return a new field on APContact that has recordIDs of linked contacts (or strong relationships to the linked APContact objects). Unfortunately, it does not appear that Apple's ABAddressBookCopyArrayOfAllPeople() method returns the unified contact, so if you stop returning linked contacts beyond the first, there could be other non-shared fields that are missing.
Sorry for dealy. I'll try to reproduce the steps and think about best solution
I am also getting duplicate contacts when using APAddressBook:

(The screenshots are from the built-in Contacts app, the black stuff is me logging out APContact fields from contacts I got back when using your library)
Is there any way to get the underlying record so I can inspect the links myself and sort out what is linked and what is not?
Does annyone have a soulution yet ?
I think it can be filtered with ABPersonCopyArrayOfAllLinkedPeople. I'll try to make a PR for this
seems that its already implemented on #39 and was released just a fews hours ago.. :)
With the latest release. I managed to filter unique contacts with this:
APAddressBook *addressBook = [[APAddressBook alloc] init];
addressBook.fieldsMask = APContactFieldDefault | APContactFieldRecordID | APContactFieldLinkedRecordIDs;
NSMutableArray *uniqueContact = [NSMutableArray new];
addressBook.filterBlock = ^BOOL(APContact *contact) {
BOOL shouldInclude = [contact.phones count] > 0 && ![uniqueContact containsObject:contact.recordID];
if (shouldInclude) {
[uniqueContact addObject:contact.recordID];
[uniqueContact addObjectsFromArray:contact.linkedRecordIDs];
}
return shouldInclude;
};
[addressBook loadContacts:^(NSArray *contacts, NSError *error) {
// unique contacts with phone number
}];
@simonlinj I've checked you solution. It works, but have an issue with contact list order. You can reproduce it.
- Create contact named 'XXX' (for example, it has recordID 250)
- Create contact name 'YYY' (it has greater recordID because it created later)
- Link 'XXX' into 'YYY' not otherwise. So you'll see only 'YYY' record in Contacts.app
- Run your code and you'll see 'XXX', not 'YYY'. It's happened because
ABAddressBookCopyArrayOfAllPeoplereturns contacts sorted by RecordID.
So, I have no idea how Contacts.app works)) If you (or someone else) are still interesting in this feature, please share your thoughts how to implement this feature.
Hi, everybody Now I'm testing on iOS 9 and make some investigation. For testing I have created in simulator 3 additional contact, 2 of them was linked to 3th. Other contacts, exclude one, was deleted. So what I have found:
- all linked contacts stored separately (i.e. has different record with different recordID),
- there is no any merged contact in address book storage.
- every contact has linkedIDs propery, which gives info that they are merged. But I don't understand, how Contacts.app gives name for unified contact. Anybody can clarify that?
I wrote code for unifying linked contacts after retriving from store by APAddressBook library:
private func filterLinkedContacts(contacts: [AddressBookContact]) -> [AddressBookContact] {
var res = [AddressBookContact]()
var linkedDic: [Set<NSNumber> : [AddressBookContact]] = Dictionary()
for contact in contacts {
if contact.linkedRecordIDs?.count > 0 {
var linkedContactIDs = contact.linkedRecordIDs!
linkedContactIDs.append(contact.recordID)
let linkS = Set(linkedContactIDs)
if var linkedContacts = linkedDic[linkS] {
linkedContacts.append(contact)
linkedDic[linkS] = linkedContacts
} else {
linkedDic[linkS] = [contact]
}
} else {
res.append(contact)
}
}
for (_, linkedContacts) in linkedDic {
let unified = unifyLinkedContacts(linkedContacts)
res.append(unified)
}
return res
}
private func unifyLinkedContacts(linkedContacts: [AddressBookContact]) -> AddressBookContact {
let unified = linkedContacts.first!
var unifiedNums = Set<String>()
var unifiedEmails = Set<String>()
for subLink in linkedContacts {
if let nums = subLink.phones {
for n in nums {
unifiedNums.insert(n)
}
}
if let emails = subLink.emails {
for e in emails {
unifiedEmails.insert(e)
}
}
}
unified.emails = Array(unifiedEmails)
unified.phones = Array(unifiedNums)
return unified
}
Where AddressBookContact is wrapper for APContact (with public properties setters). So it's obvious that unified contact will be named by first entry and it can differ from Contacts.app!
@ArniDexian looks like Contacts.app use some private API. If anybody find the solution let me know