How to let user select contact from their contact list

A short look at the CNContactPickerViewController from the ContactsUI framework. Learn how to leverage this contact picker to access contact details.

Say you need your user to select particular contact (or maybe just the contact's phone number) in your app. There is a built-in controller that does just that. It is called CNContactPickerViewController and you can use it after importing ContactsUI.

In this post I would like to show a basic usage of this controller. It works via delegate protocol, so it is similar to selecting images, working with calendar controller etc. You can use this controller to select either one or multiple contacts (depending on your needs) or also just a contact property or properties. This means getting just the phone number, email address or other piece of contact information.

You also don't need to ask for user's permission, since this is system controller and your app only gets access to the final selection, not the list of contacts.

Contact picker usage

The strange part lies in telling the framework what kind of data we are after. When you create an instance of CNContactPickerViewController and look at the available properties, you will discover that there is no setting to enable multi-select of contacts or to specify that we just want a single piece of data.

This is done by implementing CNContactPickerDelegate. By implementing particular method, we are telling the CNContactPickerViewController how it should behave.

The basic example is selecting single contact. First create the controller, set the delegate and present it:

import ContactsUI

func presentContactPicker() {
    let contactPickerVC = CNContactPickerViewController()
    contactPickerVC.delegate = self
    present(contactPickerVC, animated: true)
}

Delegate implementation

And then the delegate implementation:

func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
        // handle selection
}

This will present something very similar to the system Contacts app and after you tap on a particular contact, the controller will dismiss and the method above will get called.

If you want multi-select, you need to implement this method instead:

func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {   
}

With this method in place, the controller will look different - you get the checkboxes to select multiple contacts and button to confirm the selection.

The remaining methods for contact properties work in the same way:

func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
}
func contactPicker(_ picker: CNContactPickerViewController, didSelectContactProperties contactProperties: [CNContactProperty]) {
}

I have to say, I was quite surprised by this strange behaviour when I was exploring ContactsUI. I have not seen this previously - view controller that behaves differently based on what delegate methods are implemented. I also had no idea how this is achieved. What follows is an extra text about how (I think) this works. Feel free to skip it.

How it works behind the scenes?

After some thinking, it occurred to me that this must be Obj-C doing. Because as far as I know there is not a way to have truly optional protocol methods - apart from providing basic/empty implementation via extension. A while later I found respondsToSelector method from Obj-C that returns true if object has implemented method you pass as a parameter. Turns out you can use it from Swift also, the method is responds(to:. So I checked the docs and found the Obj-C name for the multiple contact selection method. And with this code you can check if it is implemented:

if self.responds(to: Selector("contactPicker:didSelectContacts:")) {
      print("Implemented method to select multiple contacts")
}

This will print the info if the method to select multiple contacts is implemented.

Uses: Xcode 12 & Swift 5.3

Filip Němeček profile photo

WRITTEN BY

Filip Němeček @nemecek_f

iOS blogger and developer with interest in Python/Django. Telling other devs' stories with iOS Chat.