CNContact Tutorial: Retrieve information from a user's address book in iOS 9 and Swift 2.2

The app I'm currently working on asks the user to enter an emergency contact, including name, phone number, email address, and relationship. I thought it would be easier if I could let the user choose a contact from their address book and have that pre-fill the UITextFields since this would give them the option to either manually enter the information or choose a contact and have the ability to edit the information as needed.

Getting Started

The first thing you need to do is set up a single page project. You can call it whatever you want, mine is called "CNContacts Example" I set up the view controller like this with 1 button, 8 labels, and 8 textfields:

I then set up the following outlet references:

As you can see, we added references for every textfield: Name, Phone 1-3, Email 1-3, and Relationship. We also added references to the labels for Phone 1-3 and Email 1-3, this will come into play later. You can also name these whatever you want, but it will be easier to transfer the code if you use the same names,

There are two frameworks that we will be using in this project, Contacts and ContactsUI. In your ViewController, above the class, enter the following code:

  import Contacts  
  import ContactsUI  

You will also wanted to add CNContactPickerDelegate to your class, by adding the following:

  class ViewController: UIViewController, CNContactPickerDelegate { 


The Code

The first thing you want to happen when someone opens your app for the first time is for the user to grant permission to access their contacts. You can do this by adding the following function to your code:

    func askForContactAccess() {  
      let authorizationStatus = CNContactStore.authorizationStatusForEntityType(CNEntityType.Contacts)  
      switch authorizationStatus {  
      case .Denied, .NotDetermined:  
        self.contactStore.requestAccessForEntityType(CNEntityType.Contacts, completionHandler: { (access, accessError) -> Void in  
          if !access {  
            if authorizationStatus == CNAuthorizationStatus.Denied {  
              dispatch_async(dispatch_get_main_queue(), { () -> Void in  
                let message = "\(accessError!.localizedDescription)\n\nPlease allow the app to access your contacts through the Settings."  
                let alertController = UIAlertController(title: "Contacts", message: message, preferredStyle: UIAlertControllerStyle.Alert)  
                let dismissAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) { (action) -> Void in  
                self.presentViewController(alertController, animated: true, completion: nil)  

You're going to get an error from "contactStore", go ahead and add the following within the ViewController class (I entered it below the label references):

    var contactStore = CNContactStore()  

Now in order to get the askForContactAccess to actually happen, you need to add "self.askForContactAccess()" to the viewDidLoad function so it looks like this:

    override func viewDidLoad() {  

The next thing we need to is add an action for our button. The button will pull up the user's list of contacts. Within the IBAction, add the following code:

     let contactPicker = CNContactPickerViewController()  
     contactPicker.delegate = self  
     self.presentViewController(contactPicker, animated: true, completion: nil)

Within this code, you are declaring the ViewController as a delegate to present the "contactPicker" view controller, which will display the user's list of contacts.

As of right now, we can bring up the contact list, but nothing will happen when one is selected. Now we need to create the actual function "contactPicker" to tell the app what to do when a contact is selected. Add the following code the ViewController:

 func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {  
     NSNotificationCenter.defaultCenter().postNotificationName("addNewContact", object: nil, userInfo: ["contactToAdd": contact])  

Great, we he have the function! Now what?
In order to fill these fields, we need to know how to get 4 pieces of information

  1. First name
  2. Last name
  3. Phone numbers
  4. Email addresses

Let's look it what happens when you pull a contact's information. This function calls an item called "contact". If the only code you entered into the above function was "print(contact)", the log would print something like this:

<CNContact: 0x7fbe721302a0: identifier=EF087737-DE17-4C30-84DB-B997D59E13E2, givenName=Kate, familyName=Bell, organizationName=Creative Consulting, phoneNumbers=(    "<CNLabeledValue: 0x7fbe70513a50: identifier=44F2A168-0C77-4C5C-9905-921B83CEDCBB, label=_$!<Mobile>!$_, value=<CNPhoneNumber: 0x7fbe70523650: countryCode=us, digits=5555648583>>",    "<CNLabeledValue: 0x7fbe705012c0: identifier=77ABFC95-8916-4D3B-AF0C-ADE7088CEBD6, label=_$!<Main>!$_, value=<CNPhoneNumber: 0x7fbe70513780: countryCode=us, digits=4155553695>>"), emailAddresses=(    "<CNLabeledValue: 0x7fbe72166930: identifier=A86750FD-7496-4169-8DF8-09E8507B7BF9, label=_$!<Work>!$_,>",    "<CNLabeledValue: 0x7fbe7214b0d0: identifier=11188900-112A-422B-AA07-2365AABBB52C, label=_$!<Work>!$_,>"), postalAddresses=(    "<CNLabeledValue: 0x7fbe70517380: identifier=5B898E66-F3FD-49F7-949C-35CB01294351, label=_$!<Work>!$_, value=<CNPostalAddress: 0x7fbe7051a8a0: street=165 Davis Street, city=Hillsborough, state=CA, postalCode=94010, country=, countryCode=us, formattedAddress=(null)>>")>

As you can see, this shows information for name, emails, phone number, and mailing address (which I'm not currently interested in) But how do we turn this into usable information?

Pulling information from the contact data

To get the name information

To get the first name: contact.givenName - This returns "Kate"
To get the last name: contact.familyName - This returns "Bell"
To turn this into a full name: contact.givenName + " " + contact.familyName - This returns "Kate Bell"

self.txtName.text = contact.givenName + " " + contact.familyName  

That was fairly easy, wasn't it? Well getting the phone numbers and email addresses is a little trickier, so let's go ahead and break those down.

To get the phone number(s)

You can get the contact's phone information by calling contact.phoneNumbers, which looks like this:

[<CNLabeledValue: 0x7f91dab8fef0: identifier=44F2A168-0C77-4C5C-9905-921B83CEDCBB, label=_$!<Mobile>!$_, value=<CNPhoneNumber: 0x7f91dab0be10: countryCode=us, digits=5555648583>>, <CNLabeledValue: 0x7f91dab09c30: identifier=77ABFC95-8916-4D3B-AF0C-ADE7088CEBD6, label=_$!<Main>!$_, value=<CNPhoneNumber: 0x7f91daba30c0: countryCode=us, digits=4155553695>>]

This is an array array of 2 phone numbers along with any relevant information, so the first step is to pull one of numbers out of the array with contact.phoneNumbers[0], which will look like:

<CNLabeledValue: 0x7fcb3ad73cd0: identifier=44F2A168-0C77-4C5C-9905-921B83CEDCBB, label=_$!<Mobile>!$_, value=<CNPhoneNumber: 0x7fcb3ad55350: countryCode=us, digits=5555648583>>

What we're looking for here is the 'digits' field. We can get this with "(contact.phoneNumbers[0].value as!CNPhoneNumber).valueForKey("digits")"

self.txtP1.text = (contact.phoneNumbers[0].value as! CNPhoneNumber).valueForKey("digits") as? String

you can then repeat this as txtP2.text = contact.phoneNumbers[1], etc.

However, if there are no phone numbers, reading contact.phoneNumbers[0] will cause a crash. If there is only one phone number, reading contact.phoneNumbers[1] will cause a crash, and so on and so forth, so we need to make sure the numbers exist before we actually send them to the text field.

     if contact.phoneNumbers.count > 0 {  
       self.txtP1.text = (contact.phoneNumbers[0].value as! CNPhoneNumber).valueForKey("digits") as? String  
     } else {  
       self.txtP1.text = ""  

"if contact.phoneNumbers.count > 0" checks that there is at least 1 item in the array before performing the code. Because my app uploads textfield data to a server, I personally needed to enter the self.P1.text = "" into the 'else' section so it wouldn't be empty, but this is unnecessary if you are simply displaying the information.

Changing the labels

I also wanted to change the labels from "Phone 1", "Phone 2"... to "Home", "Cell", or whatever type of phone number it was.

We can achieve this with "contact.phoneNumbers[0].label", which, in this case, returns:


So now we just have to get rid of those extra characters. I was able to separate "Mobile" with the following code:

       let pLabel = contact.phoneNumbers[0].label //_$!<Mobile>!$_  
       let pLabel2 = pLabel.characters.split("<").map(String.init) //[_$!<, Mobile>!$_]  
       let pLabel3 = pLabel2[1].characters.split(">").map(String.init) //[Mobile, >!$_]  
       self.lblP1.text = pLabel3[0] //Mobile  

Additionally, in the else section, I added "self.lblP1.text = "Phone 1". If the user chooses a contact that has a phone number, and then chooses another contact that does not have a phone number, this returns the label to the original text.

So now, the entire text for the phone number looks like this:

     if contact.phoneNumbers.count > 0 {  
       self.txtP1.text = (contact.phoneNumbers[0].value as! CNPhoneNumber).valueForKey("digits") as? String  
       let pLabel = contact.phoneNumbers[0].label //_$!<Mobile>!$_  
       let pLabel2 = pLabel.characters.split("<").map(String.init) //[_$!<, Mobile>!$_]  
       let pLabel3 = pLabel2[1].characters.split(">").map(String.init) //[Mobile, >!$_]  
       self.lblP1.text = pLabel3[0] //Mobile  
     } else {  
       self.txtP1.text = ""  
       self.lblP1.text = "Phone 1"  

This can then be repeated with slight modifications for phone 2 and 3.

To get the email(s)

This will be similar to the phone numbers but I will still break it down a bit.

contact.emailAddresses returns:

[<CNLabeledValue: 0x7fccd3e2ad30: identifier=A86750FD-7496-4169-8DF8-09E8507B7BF9, label=_$!<Work>!$_,>, <CNLabeledValue: 0x7fccd3d58a30: identifier=11188900-112A-422B-AA07-2365AABBB52C, label=_$!<Work>!$_,>]

Now I should mention that the contact I am using in this tutorial is one of the pre-loaded contacts in the simulator. If you read the above results, you will see that there are 2 work emails listed, one of which is """. I don't know why, it just is. I chose this contact for the tutorial because it is the only pre-loaded contact with multiple phone entries and multiple email entries.

contact.emailAddresses[0] returns:

<CNLabeledValue: 0x7fc1024a6b50: identifier=A86750FD-7496-4169-8DF8-09E8507B7BF9, label=_$!<Work>!$_,>

To get the email address into the textfield:

       self.txtE1.text = "\(contact.emailAddresses[0].value)" 

the rest is exactly the same as the phone numbers, so your code for Email 1 should look like this:

     if contact.emailAddresses.count > 0 {  
       self.txtE1.text = "\(contact.emailAddresses[0].value)"  
       let eLabel = contact.emailAddresses[0].label //_$!<Work>!$_  
       let eLabel2 = eLabel.characters.split("<").map(String.init) //[_$!<, Work>!$_]  
       let eLabel3 = eLabel2[1].characters.split(">").map(String.init) //[Work, >!$_]  
       self.lblE1.text = eLabel3[0] //Work  
     } else {  
       self.txtE1.text = ""  
       self.lblE1.text = "Email 1"  

And that's it! Just repeat the code and make the necessary adjustments for Email 2 and Email 3.

What your final result should look like:

The full project can be found on GitHub here.