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.
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,
You will also wanted to add CNContactPickerDelegate to your class, by adding the following:
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:
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):
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:
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:
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:
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
- First name
- Last name
- Phone numbers
- 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>!$_, firstname.lastname@example.org>", "<CNLabeledValue: 0x7fbe7214b0d0: identifier=11188900-112A-422B-AA07-2365AABBB52C, label=_$!<Work>!$_, value=www.icloud.com>"), 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"
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, 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.value as!CNPhoneNumber).valueForKey("digits")"
you can then repeat this as txtP2.text = contact.phoneNumbers, etc.
However, if there are no phone numbers, reading contact.phoneNumbers will cause a crash. If there is only one phone number, reading contact.phoneNumbers 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" 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.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:
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:
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.
[<CNLabeledValue: 0x7fccd3e2ad30: identifier=A86750FD-7496-4169-8DF8-09E8507B7BF9, label=_$!<Work>!$_, email@example.com>, <CNLabeledValue: 0x7fccd3d58a30: identifier=11188900-112A-422B-AA07-2365AABBB52C, label=_$!<Work>!$_, value=www.icloud.com>]
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 "www.icloud.com"". 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.
<CNLabeledValue: 0x7fc1024a6b50: identifier=A86750FD-7496-4169-8DF8-09E8507B7BF9, label=_$!<Work>!$_, firstname.lastname@example.org>
To get the email address into the textfield:
the rest is exactly the same as the phone numbers, so your code for Email 1 should look like this:
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.