How to find CNContact from array of contacts for a given email or phone number?

This is what I need to achieve (it is a proof of concept for now):

class ContactManager {
static var shared = ContactManager()
private let store = CNContactStore()
private var contacts = [CNContact]()
init() {
    do {
        let keys: [CNKeyDescriptor] = [
            CNContactGivenNameKey as CNKeyDescriptor,
            CNContactFamilyNameKey as CNKeyDescriptor,
            CNContactEmailAddressesKey as CNKeyDescriptor,
            CNContactPhoneNumbersKey as CNKeyDescriptor]
        contacts = try store.unifiedContacts(matching: NSPredicate(value: true), keysToFetch: keys)
    } catch {

    }
}
func contact(with email: String) -> CNContact? {
    return contacts.filter { $0.emailAddresses.contains(CNLabeledValue<String>(label: nil, value: email))}.first
}
}

I need to use it like this:

var contact = ContactManager.shared.contact(with: "bartekss2@gmail.com")

How can I filter my already fetched array of contacts to find proper contact?

1 answer

  • answered 2019-04-21 12:16 Ahmad F

    Assuming that you already got the permission for fetching the contact (added NSContactsUsageDescription in the info.plist file and the user allowed it), regardless of how you would implement ContactManager, you have to enumerateContacts first and find the desired contact. As an example of how you could implement contact(with:) method:

    func contact(with email: String, completion: @escaping (_ contact: CNContact?)->Void) {
        let keys: [CNKeyDescriptor] = [
            CNContactGivenNameKey as CNKeyDescriptor,
            CNContactFamilyNameKey as CNKeyDescriptor,
            CNContactEmailAddressesKey as CNKeyDescriptor,
            CNContactPhoneNumbersKey as CNKeyDescriptor]
        let request = CNContactFetchRequest(keysToFetch: keys)
        do {
            try store.enumerateContacts(with: request) { contact, stop in
                if String(contact.emailAddresses.first?.value ?? "") == email {
                    completion(contact)
                    stop.pointee = true
                }
            }
        } catch {
            print(error)
            completion(nil)
        }
    }
    

    thus call it as:

    ContactManager.shared.contact(with: "kate-bell@mac.com") { contact in
        print(contact)
    }
    

    Since we are dealing with enumerateContacts (async => @escaping closure) note that contact(with:) should contain a completion handler which contains the contact instead of direct return from the method itself.