2012-07-05 8 views
38

Niektórzy użytkownicy bety pod numerem my upcoming app zgłaszają, że lista kontaktów zawiera dużo duplikatów. Używam wyniku z ABAddressBookCopyArrayOfAllPeople jako źródła danych dla mojego spersonalizowanego widoku tabeli kontaktów, a to zbija mnie z błędu, że wyniki różnią się od aplikacji "Kontakt" iPhone'a.Radzenie sobie z duplikatami kontaktów ze względu na połączone karty w iOS 'Address Book API

Przyglądając się bliżej aplikacji Kontakty, wygląda na to, że duplikaty pochodzą z wpisów z "Połączonymi kartami". Poniższe zrzuty ekranu zostały nieco zaciemnione, ale jak widać w mojej aplikacji po prawej stronie, "Celine" pojawia się dwa razy, podczas gdy w aplikacji Kontakty po lewej jest tylko jedna "Celine". Jeśli klikniesz wiersz tego pojedynczego kontaktu, otrzymasz kartę "Unified Info" z dwoma "połączonymi kartami" (jak pokazano na środku, nie użyłem danych kontaktowych Celine, ponieważ nie pasowały one do jednego zrzutu ekranu):

Screenshot

kwestie wokół „Linked Cards” mają quitefew topics na Apple's forums dla użytkowników końcowych, ale oprócz faktu, że wiele wskazują na 404 support page, nie mogę realnie obejść mocowania wszystkich książki adresowe użytkowników mojej aplikacji. Wolałbym raczej uporać się z tym elegancko i bez przeszkadzania użytkownikowi. Co gorsza, wydaje mi się, że nie jestem jedynym z tego problemu, ponieważ WhatsApp is showing the same list containing duplicate contacts.

Aby być czystym o pochodzeniu zduplikowanych kontaktów, nie przechowuję, nie buforuję lub w inny sposób staram się być inteligentnym w odniesieniu do zwrotów z tablicy ABAddressBookCopyArrayOfAllPeople. Tak więc zduplikowane rekordy pochodzą bezpośrednio z wywołania API.

Czy ktoś wie, jak radzić sobie z tymi połączonymi kartami lub wykrywać je, zapobiegając wyświetlaniu duplikatów? Aplikacja Kontakty Apple to robi, jak reszta z nas może to zrobić?

AKTUALIZACJA: Napisałem bibliotekę i umieściłem ją na Cocoapods, aby rozwiązać problem. Zobacz moją odpowiedź poniżej

+1

myślę, że jest to większy problem w iOS6, ze stykami Facebooku. Ale odkąd jestem z powrotem w iOS5 nie mogę sprawdzić, czy to jest to samo .. – Jankeesvw

+0

Facebook prawdopodobnie dodaje także połączone karty ... Wtedy ktoś musiał pomyśleć o właściwym sposobie wyświetlania listy, jeśli to nie jest ABAddressBookCopyArrayOfAllPeople – epologee

Odpowiedz

5

Podejście pod warunkiem że @Daniel Amitay zawarte bryłki o wielkiej wartości, ale niestety kod nie jest gotowy do użycia. Dobre wyszukiwanie w kontaktach ma kluczowe znaczenie dla mojej i wielu aplikacji, więc poświęciłem sporo czasu, aby to naprawić, a na stronie zwrócono się również do kwestii kompatybilności z książką adresową zgodną z systemem iOS 5 i 6 (obsługa dostępu użytkownika za pomocą bloków). Rozwiązuje zarówno wiele połączonych kart z powodu nieprawidłowo zsynchronizowanych źródeł, jak i kart z nowo dodanej integracji z Facebookiem.

Biblioteka, którą napisałem, wykorzystuje przechowywany w pamięci rdzeniowej (opcjonalnie na dysku) zapis danych identyfikacyjnych książki adresowej, zapewniając łatwy algorytm wyszukiwania z gwintem tła, zwracający ujednolicone karty książki adresowej.

Źródłem jest dostępny na github repository of mine, który jest CocoaPods pod:

pod 'EEEUnifiedAddressBook' 
+0

404 w repozytorium, czy go przesunąłeś? – andreacipriani

+0

Ach, tak, ponieważ został przeniesiony do oddzielnego repozytorium. Potrzebuję trochę aktualizacji, obawiam się. – epologee

33

Jednym sposobem byłoby tylko odzyskać kontakty z domyślnego źródła Książka adresowa:

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

Ale to lame, prawda? Jest kierowany na książkę adresową na urządzeniu, ale nie na dodatkowe kontakty, które mogą znajdować się w Exchange lub innych wymyślnych książkach adresowych synchronizacji.

Więc oto rozwiązanie szukasz:

  1. iterację ABRecord odwołuje
  2. Grab każdego odpowiedniego "związane referencje" (używając ABPersonCopyArrayOfAllLinkedPeople)
  3. Bundle je w NSSet (tak, że grupa może być jednoznacznie zidentyfikowana)
  4. Dodać, że NSSet do innego NSSet
  5. Zysk?

Masz teraz NSSet zawierający NSSets połączonych obiektów ABRecord. Nadrzędny NSSet będzie miał taką samą liczbę jak liczba kontaktów w twojej aplikacji "Kontakt".

Przykładowy kod:

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]; 
+0

To kulawa, że Apple nie zapewnia tego dla nas, ale twoja ujednolicona książka adresowa rozwiązuje problem doskonale! Pozostawię pytanie otwarte na wypadek, gdyby ktoś bliżej Apple chciał zważyć, ale myślę, że +100 wkrótce będzie twoje :) Dzięki! – epologee

+0

Daniel, zastanawiam się, dlaczego usunąłeś repozytorium DAUnifiedAddressBook z GitHub. Brzmi jak coś użytecznego. –

+0

=/To był środek zapobiegający powstawaniu luk, który nie został w pełni opracowany. W rzeczywistości miał po prostu wywołanie "generateUnifiedContacts", które wykonało powyższe kroki. Miałem plany dalszego rozwijania tego, ale ostatecznie nie byłoby o wiele lepsze od zrobienia powyższego, a potem obsługiwać odniesienia zgodnie z życzeniem. –

8

Używam ABPersonCopyArrayOfAllLinkedPeople() w mojej aplikacji dla jakiegoś czasu. Niestety, właśnie odkryłem, że nie zawsze robi to dobrze. Na przykład, jeśli masz dwa kontakty, które mają tę samą nazwę, ale jedna ma ustawioną flagę "isPerson", a druga nie, powyższa funkcja nie uzna ich za "połączoną". Dlaczego to jest problem? Ponieważ źródła Gmaila (wymiany) nie obsługują tej flagi boolowskiej. Jeśli spróbujesz zapisać to jako fałszywe, spowoduje to niepowodzenie, a kontakt, który w nim zapisałeś, wróci w następnym uruchomieniu aplikacji jako odłączony od kontaktu zapisanego w iCload (CardDAV).

Podobna sytuacja z usług socjalnych: Gmail ich nie obsługuje, a funkcja powyżej będzie zobaczyć dwa kontakty o takich samych nazwach jak inna, jeśli ktoś ma konto na Facebooku, a jeden nie.

mam przełączenie na moim własnym imieniu i-source-recordID tylko do algorytmu do określenia, czy dwa rekordy kontaktowe powinny być wyświetlane w postaci pojedynczego kontaktu. Więcej pracy, ale jest srebrna podszewka: ABPersonCopyArrayOfAllLinkedPeople() działa powoli.

+1

Witamy w przepełnieniu stosu. Istnieją dwie poprzednie odpowiedzi, jedna z nich została już przegłosowana całkiem sporo, a druga z oryginalnego plakatu identyfikująca to, co faktycznie zrobili, aby rozwiązać problem. Nie jestem pewien, czy to, co proponujesz, znacznie różni się od tego, co jest przeznaczone dla innych użytkowników do pobrania Githuba, ale nie jestem pewien, czy twoja odpowiedź naprawdę pomaga, ponieważ nie jest jasne, czy inni mogą uzyskać kod rozwiązania. Nie będę głosował za tym, ale proszę zastanów się, czy udzielona przez ciebie odpowiedź dostarcza nowych informacji, gdy jest to starsze pytanie. –

+3

Myślę, że odpowiedź Christophera miała na celu dostarczenie kilku podstawowych informacji na temat możliwych luk w implementacji ABPersonCopyArrayOfAllLinkedPeople(). Nie widziałem tych informacji w innym miejscu, więc uważam, że może to być przydatne dla mnie lub innych osób. –

+0

Uważam, że jest to bardzo wyczerpująca odpowiedź i zawiera informacje, które w przeciwnym razie byłyby trudne do uzyskania, odnoszące się do pytania i wcześniej udzielonych odpowiedzi. Rewizja. – Tim

0

Dostaję wszystkie źródła ABAddressBookCopyArrayOfAllSources, przesuwając domyślny ABAddressBookCopyDefaultSource do pierwszej pozycji, następnie iterację nich i uzyskanie wszystkich ludzi ze źródła ABAddressBookCopyArrayOfAllPeopleInSource omijając te Widziałem powiązanych wcześniej, potem coraz połączonych ludzi na każdym ABPersonCopyArrayOfAllLinkedPeople.

4

Dzięki nowemu modelowi iOS 9 Contacts możesz w końcu uzyskać zunifikowane kontakty.

pokażę wam dwa przykłady:

1) za pomocą szybkiej wyliczanie

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

i tu pętlę przez wszystkie ujednoliconej kontaktów za pomocą szybkiego wyliczenie:

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) za pomocą unifiedContactsMatchingPredicate API :

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

P.S Użytkownik może być także zainteresowany w tym nowym 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; 
Powiązane problemy