Contacts Framework Tutorial for iOS [FREE]
source link: https://www.tuicool.com/articles/IZjiAnM
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
One of a mobile device’s most integral functions is storing contact information for people you want to communicate with. Whether it be through phone calls, messaging or sharing very important memes, your device would be useless for communicating without this information. You can get this information with the Contacts framework.
As an app developer, it’s important to understand how you can use contact information to better integrate your app with the user’s device. In this Contacts framework tutorial, you’ll learn how to use both the Contacts and ContactsUI frameworks.
The Contacts framework allows you to read or modify the user’s contacts from your apps. These are the same contacts that show up in the Contacts app. It replaces the old Address Book framework.
For this tutorial, you’ll be working on a modified version of an app used in a previous screen cast. You’ll be able to select, display, save and edit contacts stored on the user’s device.
Getting Started
Use the Download Materials button at the top or bottom of this tutorial to download the starter project. The app has some basic interface to show a list of default contacts. There are some buttons that you’ll be implementing actions for, so not everything works in the UI yet.
Build and run the project, and you’ll see a list of famous contacts that have been hard-coded in the app.
Before you can work with contacts, you’ll want to become familiar with CNContact
, the class used to store all of the contact’s information your app has the potential to access. Open Friend.swift
and look around. Friend
, a class for this project, has the property contactValue
.
Showing Contacts Information
If you’re working with contacts, chances are you’ll want to show this information at some point. Apple has provided a built-in view controller that can display a CNContact
with very little work.
Open FriendsViewController.swift and add this import at the top of the file:
import ContactsUI
Next, add this code to tableView(_:didSelectRowAt:)
:
// 1 let friend = friendsList[indexPath.row] let contact = friend.contactValue // 2 let contactViewController = CNContactViewController(forUnknownContact: contact) contactViewController.hidesBottomBarWhenPushed = true contactViewController.allowsEditing = false contactViewController.allowsActions = false // 3 navigationController?.navigationBar.tintColor = .appBlue navigationController?.pushViewController(contactViewController, animated: true)
Here’s what you’ve added:
-
You got the
CNContact
value from the friend. -
You created a
CNContactViewController
and configured it. Here, you turned off editing and actions. If you wanted the UI to show buttons which would allow you to share the contact or your location with the person, you can setallowsActions
totrue
. -
The
CNContactViewController
has a lighter colored theme, much the same as when you view a contact in Contacts.app . Because of the app’s theme, you need to adjust the navigation bar’s tint before presenting the contact controller, so the navigation buttons will be visible. The color is already switched back inviewWillAppear(_:)
.
Build and run and select any of the contacts in the list. You’ll now see the contact presented in a nice view controller without much work from you at all.
Picking Contacts
Next, you can take even more advantage of the ContactsUI framework by using the contact picker. It’s the built-in way to select a contact from the user’s device and import the data into your app.
Open FriendsViewController.swift and add the following code to the bottom of the file:
//MARK: - CNContactPickerDelegate extension FriendsViewController: CNContactPickerDelegate { func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) { let newFriends = contacts.compactMap { Friend(contact: $0) } for friend in newFriends { if !friendsList.contains(friend) { friendsList.append(friend) } } tableView.reloadData() } }
Here, you make sure the contact picked from the CNContactPickerViewController
is properly added to the list, but only if they weren’t previously added.
Finally, add the following implementation to addFriends(sender:)
:
// 1 let contactPicker = CNContactPickerViewController() contactPicker.delegate = self // 2 contactPicker.predicateForEnablingContact = NSPredicate( format: "emailAddresses.@count > 0") present(contactPicker, animated: true, completion: nil)
Here’s a quick breakdown of what you added:
- You created a contact picker and set the delegate.
- You set a predicate on the contact picker. You can use a predicate to filter contacts that meet a certain criteria of your choosing. In this case, you ensure that the contacts to choose from have at least one email address.
Build and run, then select the add button from the top right.
If you’re running in the simulator, you’ll see a list of the default contacts with David Taylor grayed out. This is because his contact has no email address.
Select one of the other contacts from the list and then Done . You should see the contact is in the list with the default contacts.
Setting Up Contacts Permissions
Up to this point, you were able to access the user’s contacts data without asking permission to do so. You can do that because of what Apple calls out-of-process pickers . The picker runs outside your app process and the only information you get back is the data selected by the user.
For the next steps, you’ll access the contacts store directly. You’ll need the proper permissions to do that.
First, open Info.plist and select the add button next to any of the items in the property list. In the newly added item, find or type:
Privacy - Contacts Usage Description
In the value field, add the following description:
Access is needed to save your RWConnect friends information to your Contacts list.
The system will show this property’s value to the user when presenting the alert asking for permission.
Note : Make sure to explain the reason you need that permission properly, because vague purpose strings can lead to rejections in app review.
Next, open AppDelegate.swift and add the import for Contacts to the top of the file:
import Contacts
Finally, add the following code to application(_:didFinishLaunchingWithOptions:)
right before it returns:
CNContactStore().requestAccess(for: .contacts) { (access, error) in print("Access: \(access)") }
This code will trigger the system to ask for the user’s permission for access to Contacts.
Build and run the project and you’ll see the permission alert with the text value added in Info.plist . Be sure you select OK .
Editing Contacts
In this final section, you’re going to edit a contact and save it back to the user’s device. Up to this point, you’ve been using the built in UI provided by the ContactsUI framework. Now, you are going to use a custom UI to edit or add a phone number to a contact and save it back to the device’s contact record.
To do this, you’re going to fetch the contact from the device and check if a phone number is already present, then display it in EditFriendTableViewController
.
In FriendsViewController.swift
, add the following code to prepare(for:sender:)
:
if segue.identifier == "EditFriendSegue", // 1 let cell = sender as? FriendCell, let indexPath = tableView.indexPath(for: cell), let editViewController = segue.destination as? EditFriendTableViewController { let friend = friendsList[indexPath.row] // 2 let store = CNContactStore() // 3 let predicate = CNContact.predicateForContacts(matchingEmailAddress: friend.workEmail) // 4 let keys = [CNContactPhoneNumbersKey as CNKeyDescriptor] // 5 if let contacts = try? store.unifiedContacts(matching: predicate, keysToFetch: keys), let contact = contacts.first, let contactPhone = contact.phoneNumbers.first { // 6 friend.storedContact = contact.mutableCopy() as? CNMutableContact friend.phoneNumberField = contactPhone friend.identifier = contact.identifier } editViewController.friend = friend }
Here’s what you added:
-
This gets the
FriendCell
selected from the table view. -
You create a
CNContactStore
. This is the class that allows you to read and write contacts through fetch and save requests . -
You use the class method available on
CNContact
to make a predicate that will filter on the friend’s work email. - This creates an array of keys you want to fetch from the store. Since you are going to edit the phone number, this is the only key you’ll add.
-
Next, you perform the fetch on the store. You can see
predicate
andkeys
passed to the fetch here. -
Last, if a contact matches the predicate, you create a mutable contact with the phone number added. You need to use an instance of
CNMutableContact
when you want to edit contact information. You’ll also notice an identifier gets added to the contact— you’ll use this later on.
Build and run. Select the info accessory, the i
button, on any of the contacts and see EditFriendTableViewController
now has the contact information populated.
You may notice the phone number field is still empty. That’s because none of the default contacts have phone numbers on them – yet.
Add one of the device’s contacts to the friends list and then select the info accessory. You can see that a phone number is also populated.
In this last step, you’re going to save contact information to the device. Open EditFriendTableViewController.swift
and add the following to the end of save(_:)
:
let store = CNContactStore() guard let friend = friend, let phoneNumberText = phoneTextField.text else { return } let phoneNumberValue = CNPhoneNumber(stringValue: phoneNumberText) let saveRequest = CNSaveRequest()
Here, you created a CNContactStore
and ensured that you have text that you can save to a phone number. When working with contacts, you can’t save a String
type as the phone number so you need a CNPhoneNumber
type.
Finally, since you’re going to be saving information, you need a CNSaveRequest
. You’ll decide if a contact needs adding or updating with this request object.
Next, add the following after what you just added:
if let storedContact = friend.storedContact, let phoneNumberToEdit = storedContact.phoneNumbers.first( where: { $0 == friend.phoneNumberField } ), let index = storedContact.phoneNumbers.firstIndex(of: phoneNumberToEdit) { // 1 let newPhoneNumberField = phoneNumberToEdit.settingValue(phoneNumberValue) storedContact.phoneNumbers.remove(at: index) storedContact.phoneNumbers.insert(newPhoneNumberField, at: index) friend.phoneNumberField = newPhoneNumberField // 2 saveRequest.update(storedContact) friend.storedContact = nil } else if let unsavedContact = friend.contactValue.mutableCopy() as? CNMutableContact { // 3 let phoneNumberField = CNLabeledValue(label: CNLabelPhoneNumberMain, value: phoneNumberValue) unsavedContact.phoneNumbers = [phoneNumberField] friend.phoneNumberField = phoneNumberField // 4 saveRequest.add(unsavedContact, toContainerWithIdentifier: nil) }
It may look like a lot, but here’s what’s happening:
-
In the
if
condition, you are checking if you are working with a contact already saved to the device. One gotcha when working with contacts is you cannot update the phone number field directly— you have to replace it.phoneNumberToEdit
is the old phone number that was initially displayed, and gets updated with the new phone number edited by the user, then swapped out on the contact. -
Since this contact already exists and simply needs to update, you are going to pass it to
update(_:)
on theCNSaveRequest
. This is possible because of the identifier that was set in an earlier step. If the contact doesn’t already exist, trying to save would throw an error. -
The
else if
condition is for the default contacts that don’t already exist on device. Since they don’t already have phone numbers, you need to create a new phone number from scratch. You do this usingCNLabeledValue
.
You can choose from a list of different phone number labels, but in this case, you’ll save the phone number as the main phone number.
- Like the case of updating a contact, you need to tell the save request that you are going to add this contact to the device.
You’re almost done! The final step is to execute the save. At the end of save(_:)
add the final block of code:
do { try store.execute(saveRequest) let controller = UIAlertController(title: "Success", message: nil, preferredStyle: .alert) controller.addAction(UIAlertAction(title: "OK", style: .default)) present(controller, animated: true) setup() } catch { print(error) }
Here, you attempt to execute the save request on the store. If it works, you’ll see a success message.
Note
: Methods on CNContactStore
are synchronous and access the file system, so in production code you should actually run them on background threads.
Build and run and edit a default contact to have a phone number. After you save, open Contacts.app on the simulator or device and find the contact you saved. You’ll see the default information always present in the app and the phone number you added.
Next, in the app, add a contact from the device. Edit the phone number and save. Back in Contacts.app , find the contact to see the phone number you updated saved.
Where to Go From Here?
You did it! In this Contacts framework tutorial, you’ve successfully learned how to use both the Contacts and ContactsUI frameworks.
You can download the completed version of this project using the Download Materials button at the top or bottom of this tutorial.
By utilizing the Contacts framework, you can simplify tasks that require contact information or enhance the contacts experience without forcing your users to maintain separate copies of their contacts. If you’d like to learn more, you can read Apple’s documentation on the Contacts framework and ContactsUI .
There’s also a great video on Apps Privacy from Apple.
If you have any questions or comments, please join the forum discussion below.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK