How can I fetch a contact by phone number

I've been exploring with the `CNContactStore` and having problems figuring out how to fetch a contact by phone number.


Is guess I could use the NSPredicate somehow, but how would I write a predicate on phone numbers?

Answered by OOPer in 61032022

In the CNContactStore Class Reference, you can find this description:

- unifiedContactsMatchingPredicate:keysToFetch:error:

Use only the predicates from the

CNContact
class predicates. Compound predicates are not supported by this method.


And in the CNContact class reference, you can find only two predicate methods:

+ predicateForContactsMatchingName:

+ predicateForContactsWithIdentifiers:

(Other two predicate methods are for groups and containers.)


So, if you want to choose contacts by phone number, you need to retrieve all contacts and filter it programatically yourself.


You can use - enumerateContactsWithFetchRequest:error:usingBlock: to fetch all the contacts, but all the same,

Compound predicates are not supported. (in predicate of CNContactFetchRequest)

Accepted Answer

In the CNContactStore Class Reference, you can find this description:

- unifiedContactsMatchingPredicate:keysToFetch:error:

Use only the predicates from the

CNContact
class predicates. Compound predicates are not supported by this method.


And in the CNContact class reference, you can find only two predicate methods:

+ predicateForContactsMatchingName:

+ predicateForContactsWithIdentifiers:

(Other two predicate methods are for groups and containers.)


So, if you want to choose contacts by phone number, you need to retrieve all contacts and filter it programatically yourself.


You can use - enumerateContactsWithFetchRequest:error:usingBlock: to fetch all the contacts, but all the same,

Compound predicates are not supported. (in predicate of CNContactFetchRequest)

If you use the ContactsUI you can get close to the behavior that you want. You can enable all of the contacts that have phoneNumbers and disable those that have none. Then your users can search from there:


See the predicateForEnablingContact method on the CNContactPickerViewController.


phoneNumbers.@count > 0


Otherwise you will have to roll your own searchable object, which might give you something like this (note that the enumeration only adds contacts that contain phone numbers).


    func findContacts () -> [CNContact]{
  
        let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),CNContactPhoneNumbersKey] /
        let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch)
        var contacts = [CNContact]()
        CNContact.localizedStringForKey(CNLabelPhoneNumberiPhone)
        fetchRequest.mutableObjects = false
        fetchRequest.unifyResults = true
        fetchRequest.sortOrder = .UserDefault
  
        let contactStoreID = CNContactStore().defaultContainerIdentifier()
        print("\(contactStoreID)")
  
  
        do {
      
            try CNContactStore().enumerateContactsWithFetchRequest(fetchRequest) { (let contact, let stop) -> Void in
                /
                if contact.phoneNumbers.count > 0 {
                    contacts.append(contact)
                }
          
            }
        } catch let e as NSError {
            print(e.localizedDescription)
        }
  
        return contacts
  
    }


I have not tried this out yet; but you might modify the function above to run on a background thread with a completion handler and assign the output to an array and you are all set.


func findContactsOnBackgroundThread ( completionHandler:(contacts:[CNContact]?)->()) {
   
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
       
            let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),CNContactPhoneNumbersKey] /
            let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch)
            var contacts = [CNContact]()
            CNContact.localizedStringForKey(CNLabelPhoneNumberiPhone)
       
            fetchRequest.mutableObjects = false
            fetchRequest.unifyResults = true
            fetchRequest.sortOrder = .UserDefault
       
            let contactStoreID = CNContactStore().defaultContainerIdentifier()
            print("\(contactStoreID)")
       
       
            do {
           
                try CNContactStore().enumerateContactsWithFetchRequest(fetchRequest) { (contact, stop) -> Void in
                    /
                    if contact.phoneNumbers.count > 0 {
                        contacts.append(contact)
                    }
               
                }
            } catch let e as NSError {
                print(e.localizedDescription)
            }
       
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                completionHandler(contacts: contacts)
           
            })
        })
    }


OR just wrap your call to the first function using:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
            self.contacts = self.findContacts()
           
            dispatch_async(dispatch_get_main_queue()) {
               //update your UI somehow 
               //self.tableView!.reloadData()
            }
        }


Please let me know if this works for by clicking the link below or update the thread if needed.

Actually, for since iOS 11 SDK CNContactStore provides a new pre-configured NSPredicate for exactly this:


+ (NSPredicate *)predicateForContactsMatchingPhoneNumber:(CNPhoneNumber *)phoneNumber;


https://developer.apple.com/documentation/contacts/cncontact/3020511-predicateforcontactsmatchingphon?language=objc

How can I fetch a contact by phone number
 
 
Q