Merge duplicate CNCONTACT array swift 3 contacts framework

2.1k views Asked by At

I did find duplicate contacts list from this method , now i'm stuck in merging the duplicates, any idea how i can do this.

I fetched duplicate using this code Referenced from previous question.

let formatter = CNContactFormatter()
formatter.style = .fullName

let keys = [CNContactIdentifierKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: .fullName)]
let request = CNContactFetchRequest(keysToFetch: keys)
var contactsByName = [String: [CNContact]]()
try! self.store.enumerateContacts(with: request) { contact, stop in
    guard let name = formatter.string(from: contact) else { return }
    contactsByName[name] = (contactsByName[name] ?? []) + [contact]   // or in Swift 4, `contactsByName[name, default: []].append(contact)`
}
let duplicates = contactsByName.filter { $1.count > 1 }
3

There are 3 answers

1
Satish Babariya On BEST ANSWER

If you followed my previous answer for fetching duplicates list after you can use this code to merge duplicates.

func mergeAllDuplicates() -> CNContact {

    let duplicates: [Array<CNContact>] = //Array of Duplicates Contacts

    for item in duplicates {

        // CNCONTACT PROPERTIES

        var namePrefix: [String] = [String]()
        var givenName: [String] = [String]()
        var middleName: [String] = [String]()
        var familyName: [String] = [String]()
        var previousFamilyName: [String] = [String]()
        var nameSuffix: [String] = [String]()
        var nickname: [String] = [String]()
        var organizationName: [String] = [String]()
        var departmentName: [String] = [String]()
        var jobTitle: [String] = [String]()
        var phoneNumbers: [CNPhoneNumber] = [CNPhoneNumber]()
        var emailAddresses: [NSString] = [NSString]()
        var postalAddresses: [CNPostalAddress] = [CNPostalAddress]()
        var urlAddresses: [NSString] = [NSString]()

        var contactRelations: [CNContactRelation] = [CNContactRelation]()
        var socialProfiles: [CNSocialProfile] = [CNSocialProfile]()
        var instantMessageAddresses: [CNInstantMessageAddress] = [CNInstantMessageAddress]()

        // Filter
        for items in item {
            namePrefix.append(items.namePrefix)
            givenName.append(items.givenName)
            middleName.append(items.middleName)
            familyName.append(items.familyName)
            previousFamilyName.append(items.previousFamilyName)
            nameSuffix.append(items.nameSuffix)
            nickname.append(items.nickname)
            organizationName.append(items.organizationName)
            departmentName.append(items.departmentName)
            jobTitle.append(items.jobTitle)

            for number in items.phoneNumbers {
                phoneNumbers.append(number.value)
            }
            for email in items.emailAddresses {
                emailAddresses.append(email.value)
            }
            for postal in items.postalAddresses {
                postalAddresses.append(postal.value)
            }
            for url in items.urlAddresses {
                urlAddresses.append(url.value)
            }
            for relation in items.contactRelations {
                contactRelations.append(relation.value)
            }
            for social in items.socialProfiles {
                socialProfiles.append(social.value)
            }
            for message in items.instantMessageAddresses {
                instantMessageAddresses.append(message.value)
            }

        }

        let newContact = CNMutableContact()
        newContact.namePrefix = Array(Set(namePrefix))[0]
        newContact.givenName = Array(Set(givenName))[0]
        newContact.middleName = Array(Set(middleName))[0]
        newContact.familyName = Array(Set(familyName))[0]
        newContact.previousFamilyName = Array(Set(previousFamilyName))[0]
        newContact.nameSuffix = Array(Set(nameSuffix))[0]
        newContact.nickname = Array(Set(nickname))[0]
        newContact.organizationName = Array(Set(namePrefix))[0]
        newContact.departmentName = Array(Set(namePrefix))[0]
        newContact.jobTitle = Array(Set(namePrefix))[0]
        for item in Array(Set(phoneNumbers)) {
            newContact.phoneNumbers.append(CNLabeledValue(label: CNLabelHome, value: item))
        }
        for item in Array(Set(emailAddresses)) {
            newContact.emailAddresses.append(CNLabeledValue(label: CNLabelHome, value: item))
        }
        for item in Array(Set(postalAddresses)) {
            newContact.postalAddresses.append(CNLabeledValue(label: CNLabelHome, value: item))
        }
        for item in Array(Set(urlAddresses)) {
            newContact.urlAddresses.append(CNLabeledValue(label: CNLabelHome, value: item))
        }
        for item in Array(Set(contactRelations)) {
            newContact.contactRelations.append(CNLabeledValue(label: CNLabelHome, value: item))
        }
        for item in Array(Set(socialProfiles)) {
            newContact.socialProfiles.append(CNLabeledValue(label: CNLabelHome, value: item))
        }
        for item in Array(Set(instantMessageAddresses)) {
            newContact.instantMessageAddresses.append(CNLabeledValue(label: CNLabelHome, value: item))
        }

        return newContact

    }
}

This approach will take quite a memory so I suggest, use this approach as a reference.

0
Claudius Schulz On

I modified this a little bit. Maybe it helps..

extension Array where Element == String {

    var bestElement: String? {
        var options: [String : Int] = [:]

        for element in self {
            if let result = options[element] {
                options[element] = result + 1
            } else {
                options[element] = 1
            }
        }

        return options.sorted { $0.1 > $1.1 }.first?.key
    }
}


static func merge(duplicates: [CNContact]) -> CNContact {
    // EMPTY CNCONTACT PROPERTIES
    var givenName: [String] = []
    var familyName: [String] = []
    var organizationName: [String] = []
    var notes: [String] = []

    var phoneNumbers: [CNLabeledValue<CNPhoneNumber>] = []
    var emailAddresses: [CNLabeledValue<NSString>] = []
    var postalAddresses: [CNLabeledValue<CNPostalAddress>] = []
    var urlAddresses: [CNLabeledValue<NSString>] = []

    // COLLECT VALUES
    for contact in duplicates {
        givenName.append(contact.givenName)
        familyName.append(contact.familyName)
        organizationName.append(contact.organizationName)
        notes.append(contact.note)

        contact.phoneNumbers.forEach { phoneNumbers.append($0) }
        contact.emailAddresses.forEach { emailAddresses.append($0) }
        contact.postalAddresses.forEach { postalAddresses.append($0) }
        contact.urlAddresses.forEach { urlAddresses.append($0) }
    }

    // MERGE TO NEW CONTACT
    let newContact = CNMutableContact()
    newContact.givenName = givenName.bestElement ?? ""
    newContact.familyName = familyName.bestElement ?? ""
    newContact.organizationName = organizationName.bestElement ?? ""
    newContact.note = notes.joined(separator: "\n")

    newContact.phoneNumbers = phoneNumbers
    newContact.emailAddresses = emailAddresses
    newContact.postalAddresses = postalAddresses
    newContact.urlAddresses = urlAddresses

    return newContact
}
0
MatthieuARZEL On

Two things I'm asking myself while reading your function.

  1. newContact.phoneNumbers: It seems like you append ALL the numbers from the 2 contacts. If the 2 duplicated contacts have the same number, then the newContact will then have the same number twice in it's list, right?

  2. It feels like the newContact you are creating is losing lots of information. Like nickname or prefix (Doctor, etc…).

Thanks for this compact code anyway :)