点击弹窗确定按钮后如何移除表格视图中的重复联系人?
Hey Fabio, let's tackle your two questions about handling duplicate contacts when tapping the OK button, and updating your Table View accordingly. First, let's finish and refine your findDuplicateContacts() method since it was cut off, then walk through each solution step by step.
findDuplicateContacts() Method First, let's wrap up the logic to group contacts by name and identify duplicates. This will give us a clear list of contacts that need to be removed:
@objc fileprivate func findDuplicateContacts() -> [CNContact] { let keys = [ CNContactIdentifierKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: .fullName) ] let request = CNContactFetchRequest(keysToFetch: keys) var contactsByName = [String: [CNContact]]() var duplicateContacts = [CNContact]() do { try self.contactStore.enumerateContacts(with: request) { contact, stop in guard let fullName = CNContactFormatter.string(from: contact, style: .fullName) else { return } // Group contacts by their full name contactsByName[fullName, default: []].append(contact) } // Extract duplicates (keep the first contact in each group, mark the rest for deletion) for (_, contacts) in contactsByName { if contacts.count > 1 { duplicateContacts.append(contentsOf: contacts.dropFirst()) } } } catch { print("Error fetching contacts: \(error.localizedDescription)") } return duplicateContacts }
This method now returns an array of duplicate contacts (all entries except the first occurrence of each named group).
Next, add an action for your OK button. We'll use the CNContactStore to delete duplicates directly from the system contacts. Important: Ensure you've requested and been granted CNContactStoreAuthorizationType.fullAccess or writeOnly permissions first—users won't let you modify their contacts without explicit approval.
@IBAction func okButtonTapped(_ sender: UIButton) { let duplicates = findDuplicateContacts() guard !duplicates.isEmpty else { // No duplicates to remove, dismiss the popup self.dismiss(animated: true) return } // Optional: Add a user confirmation alert here to avoid accidental deletions let confirmAlert = UIAlertController(title: "Remove Duplicates?", message: "This will delete \(duplicates.count) duplicate contacts.", preferredStyle: .alert) confirmAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) confirmAlert.addAction(UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in self?.performDuplicateDeletion(duplicates: duplicates) }) present(confirmAlert, animated: true) } private func performDuplicateDeletion(duplicates: [CNContact]) { let deleteRequest = CNSaveRequest() for contact in duplicates { guard let mutableContact = contact.mutableCopy() as? CNMutableContact else { continue } deleteRequest.delete(mutableContact) } // Execute the delete request do { try contactStore.execute(deleteRequest) print("Successfully removed \(duplicates.count) duplicate contacts") // Update the Table View and dismiss the popup updateTableViewAfterDeletion() self.dismiss(animated: true) } catch { print("Error deleting duplicates: \(error.localizedDescription)") // Show an error alert to the user let errorAlert = UIAlertController(title: "Error", message: "Failed to remove duplicates: \(error.localizedDescription)", preferredStyle: .alert) errorAlert.addAction(UIAlertAction(title: "OK", style: .default)) present(errorAlert, animated: true) } }
Assuming your Table View uses an array (e.g., var contacts: [CNContact] = []) as its data source, you'll need to sync it with the updated contact store. Here are two reliable ways to do this:
fileprivate func updateTableViewAfterDeletion() { // Option 1: Refetch fresh contacts from the store (most reliable, ensures up-to-date data) let keys = [ CNContactIdentifierKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: .fullName) ] let request = CNContactFetchRequest(keysToFetch: keys) var freshContacts = [CNContact]() do { try contactStore.enumerateContacts(with: request) { contact, stop in freshContacts.append(contact) } // Update data source on main thread (UI updates must happen here) DispatchQueue.main.async { self.contacts = freshContacts self.tableView.reloadData() } } catch { print("Error refetching contacts: \(error.localizedDescription)") } // Option 2: Filter existing array (faster, if you trust your current data source is mostly up-to-date) // let duplicateIdentifiers = Set(findDuplicateContacts().map { $0.identifier }) // DispatchQueue.main.async { // self.contacts = self.contacts.filter { !duplicateIdentifiers.contains($0.identifier) } // self.tableView.reloadData() // } }
Option 1 is preferred because it pulls the latest state directly from the contact store, avoiding any inconsistencies between your local array and the system data.
Quick Notes:
- Always handle permission checks: Before attempting to modify contacts, verify your app has the right authorization using
contactStore.authorizationStatus(for: .contacts)and request it if needed. - Soft deletions: If you're worried about accidental data loss, you could first move duplicates to the user's "Recently Deleted" folder instead of permanent deletion (use
deleteRequest.add(mutableContact)with aCNContactmarked for deletion).
内容的提问来源于stack exchange,提问作者Fabio

