处理由iOS中的地址簿API中的链接卡造成的重复联系人

我即将推出的应用程序的一些testing版用户报告说,联系人列表包含大量重复的logging。 我使用ABAddressBookCopyArrayOfAllPeople的结果作为我自定义的联系人表格视图的数据源,它让我感到困惑,结果与iPhone的“联系人”应用程序不同。

当仔细查看“通讯录”应用程序时,似乎重复内容来源于“已链接卡片”的条目。 下面的屏幕截图已经被混淆了一下,但正如你在我的应用程序中最右边看到的,“Celine”显示了两次,而左侧的Contacts应用程序中只有一个“Celine”。 如果您点击单个联系人的行,您会看到一张带有两个“链接卡”的“统一信息”卡(如中间所示,因为他们不适合在一个屏幕截图上,所以没有使用塞琳的联系方式)

截图

关于“链接卡片”的问题在苹果为最终用户提供的论坛上有不less 话题 ,但除了许多指向404支持页面的事实之外,我实际上并没有解决所有应用程序用户的通讯录问题。 我宁愿处理它优雅,而不打扰用户。 更糟糕的是,似乎我不是唯一的这个问题,因为WhatsApp显示包含重复联系人的同一个列表 。

只是为了清楚重复的联系人的起源,我不存储,caching或以其他方式试图聪明的数组ABAddressBookCopyArrayOfAllPeople返回。 所以重复logging直接来自API调用。

有谁知道如何处理或检测这些连接的卡,防止重复logging显示? 苹果的通讯录应用程序,我们其他人也可以这样做吗?

更新:我写了一个库,并把它放在Cocoapods上解决手头的问题。 看到我的答案在下面

一种方法是只从默认地址簿源中检索联系人:

 ABAddressBookRef addressBook = ABAddressBookCreate(); NSArray *people = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeopleInSource(addressBook, ABAddressBookCopyDefaultSource(addressBook)); 

但是这是蹩脚的,对吧? 它针对的是设备上的通讯录,但不包括可能在Exchange或其他奇特的同步通讯簿中的额外联系人。

所以这里是你正在寻找的解决scheme:

  1. 通过ABRecord参考进行迭代
  2. 抓住每个“链接的引用”(使用ABPersonCopyArrayOfAllLinkedPeople
  3. 将它们捆绑在一个NSSet中(这样可以唯一地标识分组)
  4. 将该NSSet添加到另一个NSSet
  5. 利润?

您现在有一个NSSet包含NSSets链接的ABRecord对象。 总体NSSet将与您的“联系人”应用程序中的联系人数量相同。

示例代码:

 NSMutableSet *unifiedRecordsSet = [NSMutableSet set]; ABAddressBookRef addressBook = ABAddressBookCreate(); CFArrayRef records = ABAddressBookCopyArrayOfAllPeople(addressBook); for (CFIndex i = 0; i < CFArrayGetCount(records); i++) { NSMutableSet *contactSet = [NSMutableSet set]; ABRecordRef record = CFArrayGetValueAtIndex(records, i); [contactSet addObject:(__bridge id)record]; NSArray *linkedRecordsArray = (__bridge NSArray *)ABPersonCopyArrayOfAllLinkedPeople(record); [contactSet addObjectsFromArray:linkedRecordsArray]; // Your own custom "unified record" class (or just an NSSet!) DAUnifiedRecord *unifiedRecord = [[DAUnifiedRecord alloc] initWithRecords:contactSet]; [unifiedRecordsSet addObject:unifiedRecord]; CFRelease(record); } CFRelease(records); CFRelease(addressBook); _unifiedRecords = [unifiedRecordsSet allObjects]; 

我已经在我的应用程序中使用ABPersonCopyArrayOfAllLinkedPeople()一段时间了。 不幸的是,我刚刚发现,它并不总是做正确的事情。 例如,如果您有两个联系人具有相同的名称,但其中一个设置了“isPerson”标志,另一个没有设置,则上述function不会将其视为“已链接”。 为什么这是一个问题? 由于Gmail(交换)源不支持此布尔标志。 如果您尝试将其保存为false,则将失败,并且保存在其中的联系人将在下次运行时返回,与您在iCload(CardDAV)中保存的联系人未链接。

类似的情况与社会服务:Gmail不支持他们,上面的function会看到两个名称相同的联系人,如果有一个Facebook帐户,而不是。

我将切换到我自己的名称和来源loggingIDalgorithm,以确定是否将两个联系人logging显示为单个联系人。 更多的工作,但有一线希望:ABPersonCopyArrayOfAllLinkedPeople()是屁股慢。

@Daniel Amitay提供的方法包含了极具价值的金块,但不幸的是,代码还没有准备好使用。 对联系人进行良好的search对于我的和许多应用程序是至关重要的,所以我花了相当多的时间来解决这个问题,同时也解决了iOS 5和6兼容的地址簿访问问题(通过块来处理用户访问)。 它解决了由于源代码不正确以及来自新增的Facebook集成的卡而造成的许多链接卡。

我写的库使用内存(可选磁盘)核心数据存储来caching地址簿logging标识,提供一个简单的后台线程searchalgorithm,返回统一的地址簿卡。

这个源文件可以在我的github版本库中find ,这是一个CocoaPods版本 :

 pod 'EEEUnifiedAddressBook' 

有了新的iOS 9 联系人框架,您终于可以拥有统一的联系人了。

我给你看两个例子:

1)使用快速枚举

 //Initializing the contact store: CNContactStore* contactStore = [CNContactStore new]; if (!contactStore) { NSLog(@"Contact store is nil. Maybe you don't have the permission?"); return; } //Which contact keys (properties) do you want? I want them all! NSArray* contactKeys = @[ CNContactNamePrefixKey, CNContactGivenNameKey, CNContactMiddleNameKey, CNContactFamilyNameKey, CNContactPreviousFamilyNameKey, CNContactNameSuffixKey, CNContactNicknameKey, CNContactPhoneticGivenNameKey, CNContactPhoneticMiddleNameKey, CNContactPhoneticFamilyNameKey, CNContactOrganizationNameKey, CNContactDepartmentNameKey, CNContactJobTitleKey, CNContactBirthdayKey, CNContactNonGregorianBirthdayKey, CNContactNoteKey, CNContactImageDataKey, CNContactThumbnailImageDataKey, CNContactImageDataAvailableKey, CNContactTypeKey, CNContactPhoneNumbersKey, CNContactEmailAddressesKey, CNContactPostalAddressesKey, CNContactDatesKey, CNContactUrlAddressesKey, CNContactRelationsKey, CNContactSocialProfilesKey, CNContactInstantMessageAddressesKey ]; CNContactFetchRequest* fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:contactKeys]; [fetchRequest setUnifyResults:YES]; //It seems that YES is the default value NSError* error = nil; __block NSInteger counter = 0; 

在这里,我使用快速枚举循环所有统一的联系人:

 BOOL success = [contactStore enumerateContactsWithFetchRequest:fetchRequest error:&error usingBlock:^(CNContact* __nonnull contact, BOOL* __nonnull stop) { NSLog(@"Unified contact: %@", contact); counter++; }]; if (success) { NSLog(@"Successfully fetched %ld contacts", counter); } else { NSLog(@"Error while fetching contacts: %@", error); } 

2)使用unifiedContactsMatchingPredicate API:

 // Contacts store initialized ... NSArray * unifiedContacts = [contactStore unifiedContactsMatchingPredicate:nil keysToFetch:contactKeys error:&error]; // Replace the predicate with your filter. 

PS您可能也对CNContact.h这个新的API CNContact.h

 /*! Returns YES if the receiver was fetched as a unified contact and includes the contact having contactIdentifier in its unification */ - (BOOL)isUnifiedWithContactWithIdentifier:(NSString*)contactIdentifier; 

我得到所有的来源ABAddressBookCopyArrayOfAllSources ,移动默认的一个ABAddressBookCopyDefaultSource到第一个位置,然后遍历它们,并获得来自ABAddressBookCopyArrayOfAllPeopleInSource跳过我见过链接之前,然后获得在每个ABPersonCopyArrayOfAllLinkedPeople链接的人。