Framework

Contacts

Access the user's contacts and format and localize contact information.

Overview

The Contacts framework provides Swift and Objective-C API to access the user’s contact information. Because most apps read contact information without making any changes, this framework is optimized for thread-safe, read-only usage.

Working with the User’s Contacts

This framework is available on all Apple platforms and replaces the Address Book framework in iOS and macOS.

Contact Objects

The contact class (CNContact) is a thread-safe, immutable value object of contact properties, such as the contact’s name, image, or phone numbers.

Organizational diagram showing that a contact object has a mutable variant and can have properties that are represented as labeled value objects.

The contact class is like NSDictionary in that it has a mutable subclass CNMutableContact you can use to modify contact properties. For contact properties that can have multiple values, such as phone numbers or email addresses, an array of CNLabeledValue objects is used. The labeled value class is a thread-safe, immutable tuple of labels and values. Labels describe each value to the user, allowing differentiation such as home and work phone numbers. The Contacts framework provides some predefined labels and you can create your own custom labels.

Listing 1

Creating a contact

import Contacts
 
// Creating a mutable object to add to the contact
let contact = CNMutableContact()
 
contact.imageData = NSData() // The profile picture as a NSData object
 
contact.givenName = "John"
contact.familyName = "Appleseed"
 
let homeEmail = CNLabeledValue(label:CNLabelHome, value:"john@example.com")
let workEmail = CNLabeledValue(label:CNLabelWork, value:"j.appleseed@icloud.com")
contact.emailAddresses = [homeEmail, workEmail]
 
contact.phoneNumbers = [CNLabeledValue(
    label:CNLabelPhoneNumberiPhone,
    value:CNPhoneNumber(stringValue:"(408) 555-0126"))]
 
let homeAddress = CNMutablePostalAddress()
homeAddress.street = "1 Infinite Loop"
homeAddress.city = "Cupertino"
homeAddress.state = "CA"
homeAddress.postalCode = "95014"
contact.postalAddresses = [CNLabeledValue(label:CNLabelHome, value:homeAddress)]
 
let birthday = NSDateComponents()
birthday.day = 1
birthday.month = 4
birthday.year = 1988  // You can omit the year value for a yearless birthday
contact.birthday = birthday
 
// Saving the newly created contact
let store = CNContactStore()
let saveRequest = CNSaveRequest()
saveRequest.addContact(contact, toContainerWithIdentifier:nil)
try! store.executeSaveRequest(saveRequest)

Formatting and Localization

The Contacts framework helps you format and localize contact information. For example, you can correctly format a contact name (using CNContactFormatter) or format an international postal address (using CNPostalAddressFormatter).

Listing 2

Formatting a name and postal address

// Formatting contact name
let fullName = CNContactFormatter.stringFromContact(contact, style: .FullName)
print(fullName)
// John Appleseed
 
// Formatting postal address
let postalString = CNPostalAddressFormatter.stringFromPostalAddress(homeAddress)
print(postalString)
// 1 Infinite Loop
// Cupertino
// CA
// 95014

You can display localized object property names and predefined labels, based on the current locale setting of the device. Many objects defined in the Contacts framework, such as CNContact, include the localizedString(forKey:) method, which lets you get the localized version of a key name. In addition, the CNLabeledValue class includes the localizedString(forLabel:) method, which lets you get the localized label for the predefined labels in the Contacts framework.

Listing 3

Localizing a given name

// device locale is Spanish
let displayName = CNContact.localizedStringForKey(CNContactNicknameKey)
print(displayName)
// alias 
let displayLabel = CNLabeledValue.localizedStringForLabel(CNLabelHome)
print(displayLabel)
// casa

Fetching Contacts

You can fetch contacts using the contact store (CNContactStore), which represents the user's contacts database. The contact store encapsulates all I/O operations and is responsible for fetching and saving of contacts and groups. Because the contact store methods are synchronous, it is recommended that you use them on background threads. If needed, you can safely send immutable fetch results back to the main thread.

The Contacts framework provides several ways to constrain contacts returned from a fetch, including predefined predicates and the keysToFetch property.

CNContact provides predicates for filtering the contacts you want to fetch. For example, to fetch contacts that have the name “Appleseed”, use predicateForContacts(matchingName:) and pass in Appleseed.

Listing 4

Fetching contacts using predicates

let predicate: NSPredicate = CNContact.predicateForContactsMatchingName("Appleseed")

Note that generic and compound predicates are not supported by the Contacts framework.

You can use keysToFetch to limit the contact properties that are fetched. For example, if you want to fetch only the given name and the family name of a contact, you specify those contact keys in a keysToFetch array.

Listing 5

Fetching contacts using KeysToFetch

let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]

To fetch a contact using both a predicate (predicateForContacts(matchingName:)) and a keysToFetch array, use unifiedContacts(matching:keysToFetch:).

Listing 6

Fetching contacts using a predicate and an array of contact attributes

let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName("Appleseed"), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])

The Contacts framework can also perform operations on the fetched contacts, such as formatting contact names. Each operation requires a specific set of contact keys to correctly perform the operation. The keys are specified as key descriptor objects that must be included within the keysToFetch array. For example, if you want to fetch the contact’s email addresses and also be able to format the contact’s name (using CNContactFormatter), include both CNContactEmailAddressesKey and the key descriptor object returned by descriptorForRequiredKeys(for:) in the keysToFetch array.

Listing 7

Fetching contacts with key descriptors

let keysToFetch = [CNContactEmailAddressesKey, CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName)]

Privacy

Users can grant or deny access to contact data on a per-app basis. Any call to CNContactStore will block the app while the user is being asked to grant or deny access. Note that the user is prompted only the first time access is requested; any subsequent CNContactStore calls use the existing permissions. To avoid having your app’s UI main thread block for this access, you can use either the asynchronous method requestAccess(for:completionHandler:) or dispatch your CNContactStore usage to a background thread.

Partial Contacts

A partial contact has only some of its property values fetched from a contact store. All fetched contact objects are partial contacts. If you try to access a property value that was not fetched, you get an exception. If you are unsure which keys were fetched in the contact, it is recommended that you check the availability of the property values before you access them. You can either use isKeyAvailable(_:) to check the availability of a single contact key, or areKeysAvailable(_:) to check multiple keys. If the desired keys are not available then refetch the contact with them.

Listing 8

Checking the availability of a key

// Checking if phone number is available for the given contact.
if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) {
    print("\(contact.phoneNumbers)")
} else {
    //Refetch the keys
    let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]
    let refetchedContact = try store.unifiedContactWithIdentifier(contact.identifier, keysToFetch: keysToFetch)
    print("\(refetchedContact.phoneNumbers)")
}

Unified Contacts

Contacts in different accounts that represent the same person may be automatically linked together. Linked contacts are displayed in macOS and iOS apps as unified contacts. A unified contact is an in-memory, temporary view of the set of linked contacts that are merged into one contact.

Diagram that shows a person's iCloud and Facebook contact information combined into a single, unified contact.

By default the Contacts framework returns unified contacts. Each fetched unified contact (CNContact) object has its own unique identifier that is different from any individual contact’s identifier in the set of linked contacts. A refetch of a unified contact should be done with its identifier.

Saving Contacts

The contact store (CNContactStore) is also used to save changes to the Contacts framework objects. The CNSaveRequest class enables save operations and allows batching of changes to multiple contacts and groups into a single operation. After all objects are added to the save request, it can be executed with a contact store as shown Listing 9 code listing below. Do not access the objects in the save request while the save is executing, because the objects may be modified.

Listing 9

Saving a new contact

// Creating a new contact
let newContact = CNMutableContact()
newContact.givenName = "John"
newContact.familyName = "Appleseed"
 
// Saving contact
let saveRequest = CNSaveRequest()
saveRequest.addContact(newContact, toContainerWithIdentifier:nil)
try! store.executeSaveRequest(saveRequest)
Listing 10

Saving a modified contact

let mutableContact = contact.mutableCopy() as! CNMutableContact
let newEmail = CNLabeledValue(label: CNLabelHome, value: "john@example.com")
mutableContact.emailAddresses.append(newEmail)
 
let saveRequest = CNSaveRequest()
saveRequest.updateContact(mutableContact)
try! store.executeSaveRequest(saveRequest)

Contacts Changed Notifications

After a save is successfully executed, the contact store posts a CNContactStoreDidChange notification to the default notification center. If you cache any Contacts framework objects you need to refetch those objects, either by their identifiers, or with the predicates that were used to originally fetch them, and then release the cached objects. Note that cached objects are stale, but not invalid.

Containers and Groups

A user may have contacts in their device’s local account or server accounts configured to sync contacts. Each account has at least one container of contacts. A contact can be in only one container.

Diagram that shows a person's contacts separated into a container for iCloud contacts and a container for Facebook contacts.

A group is a set of contacts within a container. Not all accounts support groups and some accounts support subgroups. An iCloud account has only one container and may have many groups but no subgroups. On the other hand, an Exchange account does not support groups, but may have multiple containers representing Exchange folders.

Diagram that shows groups within containers of contacts.

Symbols

Classes

CNContact

A thread-safe class that represents an immutable value object for contact properties, such as the first name and phone numbers of a contact.

CNContactFetchRequest

An object that defines fetching options to use while fetching contacts.

CNContactFormatter

An object that defines the different formatting styles for contacts.

CNContactProperty

An object that represents a property of a contact.

CNContactRelation

The CNContactRelation class defines an immutable value object representing a contact related to another. This is a thread-safe class.

CNContactStore

The CNContactStore class is a thread-safe class that can fetch and save contacts, groups, and containers.

CNContactsUserDefaults

The CNContactsUserDefaults class defines properties used to access the user defaults for a contact.

CNContactVCardSerialization

The CNContactVCardSerialization supports vCard representation for the given set of contacts.

CNContainer

A thread-safe class that defines an immutable object that represents a container.

CNGroup

A thread-safe class that defines an immutable object that represents a group.

CNInstantMessageAddress

A thread-safe class that defines an immutable value object representing an instant message address.

CNLabeledValue

A thread-safe class that defines an immutable value object that combines a contact property value with a label, such as a contact phone number combined with a label of Home, Work, or iPhone.

CNMutableContact

A mutable value object for the contact properties, such as the first name and the phone number of a contact.

CNMutableGroup

A mutable value object representing a group for a contact.

CNMutablePostalAddress

A mutable value object representing the postal address for a contact.

CNPhoneNumber

A thread-safe class that defines an immutable value object representing a phone number for a contact.

CNPostalAddress

A thread-safe class that defines an immutable object that represents the postal address for a contact.

CNPostalAddressFormatter

A class that formats the postal address in a contact.

CNSaveRequest

A save request operation for contacts.

CNSocialProfile

A a thread-safe class that defines an immutable object representing a social profile.

Structures