Framework

Contacts

Access users’ contacts. Format and localize contact information.

Overview

The Contacts framework provides an Objective-C and Swift 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. The contact class is like NSDictionary; 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 I-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), as shown in Listing I-2.

Listing I-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 I-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. It 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 keysToFetch arrays.

CNContact provides predicates for filtering the contacts you want to fetch. For example, to fetch contacts that have the name “Appleseed”, use predicateForContacts(matchingName:) as shown in Listing I-4.

Listing I-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 a keysToFetch array 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 an array, as shown in Listing I-5.

Listing I-5

Fetching contacts using KeysToFetch

let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]

The code snippet below shows how to fetch a contact using both a predicate (predicateForContacts(matchingName:)) and a keysToFetch array.

Listing I-6

Fetching contacts using KeysToFetch and predicates

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 descriptorForRequiredKeysForStyle in the keysToFetch array.

Listing I-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-application basis. Any call to CNContactStore will block the application 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 [CNContactStore requestAccessForEntityType:CNEntityTypeContacts 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 I-8

Checking the availability of a key using isKeyAvailable

// 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

Figure I-1

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.

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 I-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 I-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 I-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 and/or server accounts configured to sync contacts. Each account has at least one container of contacts. A contact can be in only one container.

Figure I-2

Contact containers

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.

Figure I-3

Contact groups

Symbols

Classes

CNContact

The CNContact is a thread-safe class that represents an immutable value object for contact properties, such as the first name and phone numbers of a contact. CNContact is similar to a complex Foundation collection, in that it has a mutable subclass (CNMutableContact). Neither the CNContact nor CNMutableContact class maintain a reference to their data store. Every contact has a unique ID, which you obtain using the identifier property.

CNContactFetchRequest

The CNContactFetchRequest class defines fetching options to use while fetching contacts. It is required to have contact property key(s) to fetch a contact’s properties. Use this class with the enumerateContacts(with:usingBlock:) method to execute the contact fetch request.

CNContactFormatter

The CNContactFormatter class defines the different formatting styles for contacts. This class handles international ordering and delimiting for the contact name components. When formatting many contacts, create an instance of this class and use the instance methods; otherwise use the class methods.

CNContactProperty

The CNContactProperty is a convenience class and returns a tuple or quadruple to a contact’s property. A property contains related information for a specific contact. A contact (an instance of CNContact) has properties, such as firstName, phoneNumber, and jobTitle. Each property is represented by an instance of CNContactProperty, which provides a tuple that can contain three or five values, depending on whether the property is a member of an array of labeled values. For example, the phoneNumbers property is a member of an array of labeled values, so the CNContactProperty tuple contains the contact, key, value, identifier, and label. For the givenName property, which is not contained in a labeled array, CNContactProperty returns a tuple that contains the contact, key, and value. The CNContactProperty class is used by the CNContactPicker to return the user's selected property.

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

The CNContainer class is a thread-safe class that defines an immutable object that represents a container. A container has a collection of contacts. A contact can be in only one container. CardDAV accounts usually have only one container whereas Exchange accounts may have multiple containers, where each container represents an Exchange folder

CNGroup

The CNGroup is a thread-safe class that defines an immutable object that represents a group. Contacts may be members of one or more groups, depending upon their accounts.

CNInstantMessageAddress

The CNInstantMessageAddress class is a thread-safe class that defines an immutable value object representing an instant message address. Use the methods and properties of this class to identify instant messaging address. Some instant message services, such as Facebook and Skype are predefined in this class. You can also specify your own instant message service using init(username:service:) instance method.

CNLabeledValue

The CNLabeledValue class is a thread-safe class that defines an immutable value object that combines a contact property value with a label. For example, a contact phone number could have a label of Home, Work, iPhone, etc.

CNMutableContact

The CNMutableContact class represents a mutable value object for the contact properties, such as the first name and the phone number of a contact. The CNMutableContactis not a thread-safe class. When CNMutableContact object is a mutable copy of a CNContact object, if you access a CNMutableContact property value that was not fetched for the CNContact object, it throws an CNContactPropertyNotFetchedExceptionName exception. When needed, you can remove contact properties by setting string and array properties to empty, and all other properties to nil.

CNMutableGroup

The CNMutableGroup class defines a mutable value object representing a group for a contact. Contacts may be members of one or more groups, depending upon the accounts they come from. The CNMutableGroup is not a thread-safe class.

CNMutablePostalAddress

The CNMutablePostalAddress class defines a mutable value object representing the postal address for a contact. It is not a thread-safe class. To remove properties when saving a mutable postal address, set string properties to empty values.

CNPhoneNumber

The CNPhoneNumber class defines an immutable value object representing a phone number for a contact. It is a thread-safe class.

CNPostalAddress

The CNPostalAddress class defines an immutable object that represents the postal address for a contact. This is a thread-safe class.

CNPostalAddressFormatter

The CNPostalAddressFormatter class formats the postal address in a contact. This class handles international formatting of postal addresses. It is recommended that you create an instance of this class when formatting many postal addresses, and use the instance methods; otherwise use the class methods.

CNSaveRequest

The CNSaveRequest class defines a save request operation for contacts. The CNSaveRequest class creates a new save request for each save operation on the contact store. You can batch multiple changes into one save request (note that these changes only apply to objects). In the case of overlapping changes in multiple or concurrent save requests, the last change wins. If you try to add an object (that is, a contact, or a group,) that already exists in the contact store, the CNErrorCodeInsertedRecordAlreadyExists error occurs and the CNErrorUserInfoAffectedRecordsKey array is updated to contain the object you tried to add. If you try to update or delete an object that is not present in the contact store, the save request does not perform the update or deletion, the CNErrorCodeRecordDoesNotExist error occurs, and the CNErrorUserInfoAffectedRecordsKey array is updated to contain the object you tried to update or delete. Do not access objects in the save request while a save request is executing.

CNSocialProfile

The CNSocialProfile class defines an immutable object representing a social profile. This is a thread-safe class. Some social profile services, such as Facebook and Twitter are predefined in this class. You can also specify your own social profile service with init(urlString:username:userIdentifier:service:) instance method.

Structures