WWDC 2015

Introducing the Contacts Framework

Address book frame have been using for accessing and updating user's contact books since iOS 2.0. From iOS 9, there is a new framework called Contacts that replaces addressBook.framebook, provides object orientated interfaces and additional features such as searching and filtering contacts.

Classes

CNContact

A object that represented a contact in the contact book

CNMutableContact

A object that represented a contact in the contact book and the properties are mutable. Used in adding and update contacts.

CNLabeledValue

A value of a key of certain properties of CNContact

CNSaveRequest

A cooriindater to cache any adding and updating operations to contacts

Can be cache multiple actions before executing. be aware of CNMutableContact not changing when writing into Contacts

Operations

Create contact

//Create contact
import Contacts 
// create mutable for adding to the contact
let contact = CNMutableContact()
contact.imageData = // profile picture as NSData 
contact.givenName = "John"
contact.familyName = "Appleseed"
contact.phoneNumbers = [CNLabeledValue(
    label: CNLabelPhoneNumberiPhone,
    value: CNPhoneNumber(stringValue: "(408) 555-0126"))]

let address = CNMutablePostalAddress() 
address.street = "774 Loma Vista Ave" 
address.city = "Los Gatos" 
address.state = "CA" 
address.postalCode = "95032"
contact.postalAddresses = [CNLabeledValue(label: CNLabelHome,
    value: address)]

let birthday = NSDateComponents()
birthday.day = 1
birthday.month = 4
birthday.year = 1988 // can omit for a year-less birthday

let saveRequest = CNSaveRequest()
saveRequest.addContact(john, toContainerWithIdentifier: nil)
try store.executeSaveRequest(saveRequest)

Updating A contact

let updatedContact = contact.mutableCopy()
let newEmail = CNLabeledValue(label: CNLabelHome,
    value: "[email protected]")
updatedContact.emailAddresses.append(newEmail)
let saveRequest = CNSaveRequest()
saveRequest.updateContact(updatedContact)
try store.executeSaveRequest(saveRequest)

Getting info. fron a contact

//Getting info. fron a contact
let fullName = CNContactFormatter.stringFromContact(contact, style: .FullName)
let postalString = CNPostalAddressFormatter.stringFromPostalAddress(address)

fetching user's contacts

//matching any names contains "Appleseed"
let predicate = CNContact.predicateForContactsMatchingName("Appleseed")
//getting contacts given name and family name
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
// We can also use formatter to fetch the names
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),
    CNContactEmailAddressesKey]
// get a instance of contact store. Don't need to strong reference
let store = CNContactStore()
//This may be a long running tasks, suggested to put the fetching into background threads
let contacts = try store.unifiedContactsMatchingPredicate(predicate,
    keysToFetch: keysToFetch)
for contact in contacts {
   let fullName = CNContactFormatter.stringFromContact(
        contact, style: .FullName) ?? "No Name"
    print("\(fullName): \(contact.emailAddresses)")
}

Availbility check

//call this to get permission of accessing contacts
CNContactStore.requestAccessForEntityType(_, completionHandler:)

//call this to check if the key available for fetching
if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) {
    //...
}

Contact UI

CNContactPickerViewController and CNContactViewController will replace address book ui framework

CNContactPickerViewController

  • must be presented, not pushed
  • not required contact permisson (not showing contact dislog)
  • May return partial contacts (only the names but not the phone numbers)
  • Support mult-selection
//filter the contacts in the picker
let predicate = NSPredicate(format: "familyName LIKE[cd] 'parker'")
contactPicker.predicateForEnablingContact = predicate

predicateForSelectionOfContact to indicate Which contacts are returned when tapped, others push the detail card

predicateForSelectionOfProperty to indicate Which properties are returned when tapped, others perform the default action based on CNContactProperty

Coherence between predicates and delegate methods as it does not make sense to filter the properties that you dont want to get deleget methods

CNContactViewController

viewing contacts

//init methods for different status of contact
viewControllerForContact:
viewControllerForNewContact:
viewControllerForUnknownContact:

not required contact permisson (not showing contact dislog)

let contact = try contactStore.unifiedContactWithIdentifier(identifier, keysToFetch: [CNContactViewController.descriptorForRequiredKeys])
let viewController = CNContactViewController(forContact: contact)
viewController.contactStore = self.contactStore
viewController.delegate = self
self.pushViewController(viewController)
func contactViewController(vc, didCompleteWithContact: contact) {
    // do something with the modified contact
}