2012-07-05 30 views
38

Một số người dùng beta my upcoming app đang báo cáo rằng danh sách liên hệ chứa nhiều bản ghi trùng lặp. Tôi đang sử dụng kết quả từ ABAddressBookCopyArrayOfAllPeople làm nguồn dữ liệu cho chế độ xem bảng tùy chỉnh của tôi trong danh sách liên hệ và điều này khiến tôi thấy rằng kết quả khác với ứng dụng 'Danh bạ' của iPhone.Xử lý địa chỉ liên hệ trùng lặp do thẻ được liên kết trong API sổ địa chỉ của iOS

Khi xem xét kỹ hơn ứng dụng Danh bạ, có vẻ như các bản sao có nguồn gốc từ các mục có "Thẻ được liên kết". Các ảnh chụp màn hình dưới đây đã bị làm xáo trộn một chút, nhưng như bạn thấy trong ứng dụng của tôi ở bên phải, "Celine" xuất hiện hai lần, trong khi trong ứng dụng Danh bạ ở bên trái chỉ có một "Celine". Nếu bạn nhấp vào hàng của liên hệ duy nhất đó, bạn sẽ nhận được thẻ "Thông tin hợp nhất" với hai "Thẻ được liên kết" (như được hiển thị ở giữa, tôi không sử dụng chi tiết liên hệ của Celine vì chúng không vừa với một ảnh chụp màn hình):

Screenshot

các vấn đề xung quanh "Thẻ liên kết" có quite một few topics trên Apple's forums cho người dùng cuối, nhưng ngoài thực tế là nhiều điểm đến một 404 support page, tôi có thể không thực tế đi xung quanh sửa chữa tất cả sổ địa chỉ của người dùng ứng dụng của tôi. Tôi rất muốn giải quyết nó một cách tao nhã và không làm phiền người dùng. Để làm cho vấn đề tồi tệ hơn, có vẻ như tôi không phải là người duy nhất có vấn đề này, kể từ WhatsApp is showing the same list containing duplicate contacts.

Chỉ cần làm rõ về nguồn gốc của các liên hệ trùng lặp, tôi không lưu trữ, lưu vào bộ nhớ cache hoặc nói cách khác là thông minh về mảng trả về ABAddressBookCopyArrayOfAllPeople. Vì vậy, các bản ghi trùng lặp đến trực tiếp từ cuộc gọi API.

Có ai biết cách xử lý hoặc phát hiện các thẻ được liên kết này, ngăn không cho các bản ghi trùng lặp hiển thị không? Ứng dụng Danh sách liên hệ của Apple thực hiện điều đó, làm cách nào để phần còn lại của chúng tôi cũng làm như vậy?

CẬP NHẬT: Tôi đã viết một thư viện và đặt nó trên Cocoapods để giải quyết vấn đề trong tầm tay. Xem câu trả lời của tôi dưới đây

+1

Tôi nghĩ đây là vấn đề lớn hơn trong iOS6, với các địa chỉ liên hệ trên Facebook. Nhưng kể từ khi tôi trở lại iOS5 tôi không thể xác minh nếu nó là như nhau .. – Jankeesvw

+0

Facebook có thể thêm thẻ liên kết quá ... Sau đó, ai đó phải có suy nghĩ về cách thích hợp để hiển thị một danh sách, nếu nó không phải là ABAddressBookCopyArrayOfAllPeople – epologee

Trả lời

5

Cách tiếp cận đó @Daniel Amitay cung cấp chứa cốm có giá trị lớn, nhưng tiếc là các mã chưa sẵn sàng để sử dụng. Việc tìm kiếm tốt trên các địa chỉ liên hệ rất quan trọng đối với tôi và nhiều ứng dụng, vì vậy tôi đã dành khá nhiều thời gian để làm điều này, trong khi bên cạnh cũng giải quyết vấn đề truy cập vào sổ địa chỉ tương thích iOS 5 và 6 (xử lý quyền truy cập của người dùng qua các khối). Nó giải quyết cả hai thẻ được liên kết do các nguồn được đồng bộ không chính xác và các thẻ từ tích hợp Facebook mới được thêm vào.

Thư viện tôi đã viết sử dụng kho lưu trữ dữ liệu lõi trong bộ nhớ (tùy chọn trên đĩa) để lưu trữ ID hồ sơ của sổ địa chỉ, cung cấp thuật toán tìm kiếm dạng luồng dễ dàng trả về thẻ sổ địa chỉ hợp nhất.

Các nguồn có sẵn trên một github repository of mine, mà là một pod CocoaPods:

pod 'EEEUnifiedAddressBook' 
+0

404 trên kho lưu trữ, bạn đã di chuyển chưa? – andreacipriani

+0

Ah, vâng, kể từ khi chuyển đến một kho lưu trữ riêng biệt. Nó cần một chút cập nhật, tôi sợ. – epologee

33

Một phương pháp sẽ chỉ lấy danh bạ từ các nguồn sổ địa chỉ mặc định:

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

Nhưng đó là què, phải không? Nó nhắm mục tiêu sổ địa chỉ trên thiết bị, nhưng không phải là địa chỉ liên hệ bổ sung có thể có trong Exchange hoặc các sổ địa chỉ đồng bộ hóa ưa thích khác.

Vì vậy, đây là giải pháp bạn đang tìm kiếm:

  1. Duyệt qua các ABRecord tham chiếu
  2. Grab mỗi tương ứng "tài liệu tham khảo liên kết" (sử dụng ABPersonCopyArrayOfAllLinkedPeople)
  3. Bundle chúng trong một NSSet (để nhóm có thể được xác định duy nhất)
  4. Thêm NSSet đó vào một NSSet khác
  5. Lợi nhuận?

Bây giờ bạn có một NSSet chứa NSSets của các đối tượng ABRecord được liên kết. NSSet bao quát sẽ có cùng số lượng như số liên lạc trong ứng dụng "Danh bạ" của bạn.

Ví dụ mã:

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

Đó là lame rằng Apple không cung cấp cho chúng tôi, nhưng sổ địa chỉ hợp nhất của bạn giải quyết vấn đề một cách hoàn hảo! Tôi sẽ để lại câu hỏi mở trong trường hợp bất cứ ai gần gũi hơn với Apple muốn cân nhắc, nhưng tôi nghĩ rằng +100 là sớm của bạn :) Cảm ơn! – epologee

+0

Daniel, tôi tự hỏi tại sao bạn loại bỏ kho lưu trữ DAUnifiedAddressBook khỏi GitHub. Âm thanh như một cái gì đó hữu ích. –

+0

=/Đó là một biện pháp ngăn cách mà tôi chưa phát triển đầy đủ. Trong thực tế nó chỉ đơn giản là có một cuộc gọi "generateUnifiedContacts" đã thực hiện các bước trên. Tôi đã có kế hoạch phát triển nó hơn nữa, nhưng cuối cùng nó sẽ không có được nhiều cấp trên để làm việc trên mình và sau đó xử lý các tài liệu tham khảo như mong muốn. –

8

Tôi đã sử dụng ABPersonCopyArrayOfAllLinkedPeople() trong ứng dụng của tôi trong một thời gian bây giờ. Thật không may, tôi vừa phát hiện ra rằng nó không phải lúc nào cũng làm điều đúng. Ví dụ: nếu bạn có hai địa chỉ liên hệ có cùng tên nhưng một liên kết có bộ cờ "isPerson" và thẻ còn lại thì không, hàm trên sẽ không coi chúng là "được liên kết". Tại sao điều này là một vấn đề? Vì các nguồn Gmail (trao đổi) không hỗ trợ cờ boolean này. Nếu bạn cố gắng lưu nó là sai, nó sẽ thất bại, và số liên lạc bạn đã lưu trong đó sẽ quay lại lần chạy ứng dụng tiếp theo khi hủy liên kết khỏi số liên lạc bạn đã lưu trong iCload (CardDAV).

Tình huống tương tự với dịch vụ xã hội: Gmail không hỗ trợ họ và chức năng ở trên sẽ thấy hai địa chỉ liên hệ có cùng tên khác nhau nếu một người có tài khoản facebook và người khác thì không.

Tôi đang chuyển sang thuật toán chỉ name-and-source-recordID của riêng mình để xác định xem hai bản ghi liên hệ có được hiển thị dưới dạng một liên hệ hay không. Công việc nhiều hơn nhưng có một lớp lót bạc: ABPersonCopyArrayOfAllLinkedPeople() là chậm.

+1

Chào mừng bạn đến với Stack Overflow. Có hai câu trả lời trước đó, một câu trả lời đã được bỏ phiếu khá nhiều và một câu trả lời khác từ bản gốc xác định những gì họ thực sự đã làm để giải quyết vấn đề. Tôi không chắc liệu những gì bạn đang đề xuất có khác biệt đáng kể so với những gì anh ta đặt cho người khác sử dụng trong bản tải xuống Github hay không, nhưng tôi không chắc rằng câu trả lời của bạn thực sự hữu ích vì không rõ người khác có thể nhận được mã giải pháp. Tôi sẽ không bỏ phiếu cho bạn vì điều này, nhưng hãy xem xét liệu câu trả lời bạn đưa ra có cung cấp thông tin mới khi đó là một câu hỏi cũ hơn như thế này không. –

+3

Tôi nghĩ câu trả lời của Christopher chủ yếu nhằm cung cấp một số thông tin cơ bản về những khoảng trống có thể có trong triển khai ABPersonCopyArrayOfAllLinkedPeople(). Tôi đã không thấy thông tin đó ở nơi khác, vì vậy tôi tin rằng nó có thể hữu ích cho bản thân hoặc người khác. –

+0

Tôi nghĩ rằng đây là một câu trả lời mang tính thông tin cao, và bao gồm những thông tin khó có thể đạt được, phù hợp với câu hỏi và câu trả lời trước đó. Được thăng hạng. – Tim

0

tôi nhận được tất cả các nguồn ABAddressBookCopyArrayOfAllSources, di chuyển mặc định một ABAddressBookCopyDefaultSource đến vị trí đầu tiên, sau đó lặp qua chúng và nhận được tất cả mọi người từ những nguồn ABAddressBookCopyArrayOfAllPeopleInSource bỏ qua tôi đã nhìn thấy liên kết trước đó, sau đó nhận được mọi người liên kết trên mỗi ABPersonCopyArrayOfAllLinkedPeople.

4

Với iOS 9 Khung danh bạ mới, cuối cùng bạn có thể có các liên hệ hợp nhất của mình.

tôi chỉ cho bạn hai ví dụ:

1) Sử dụng nhanh chóng liệt kê

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

Và ở đây tôi lặp qua tất cả địa chỉ liên lạc thống nhất sử dụng liệt kê nhanh:

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) Sử dụng unifiedContactsMatchingPredicate API :

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

P.S Bạn cũng có thể quan tâm đến API mới này của 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; 
Các vấn đề liên quan