Page 1
© 2015 Apple Inc. All rights reserved. Redistribution or public display not permitted without written permission from Apple.
#WWDC15
Introducing the Contacts FrameworkFor OS X, iOS, and watchOS
Bruce Stadnyk iOS Contacts EngineerDave Dribin OS X Contacts EngineerJulien Robert iOS Contacts Engineer
App Frameworks
Session 223
Page 2
What is Contacts Framework?
Page 3
What is Contacts Framework?
Objective-C and Swift API
Page 4
What is Contacts Framework?
Objective-C and Swift APIOptimized for thread-safe, read only usage
Page 5
What is Contacts Framework?
Objective-C and Swift APIOptimized for thread-safe, read only usageOne API, multiple platforms
Page 6
What is Contacts Framework?
Objective-C and Swift APIOptimized for thread-safe, read only usageOne API, multiple platformsAddressBook API being deprecated
Page 10
What are Contacts?
Page 11
What are Contacts?
Everyone has Contacts
Page 12
What are Contacts?
Everyone has Contacts Phone, Mail, Messages, …
Page 13
What are Contacts?
Everyone has Contacts Phone, Mail, Messages, …
Page 14
What are Contacts?
Everyone has Contacts Phone, Mail, Messages, … Central to the user experience
Page 15
Contact Properties
Page 16
Contact Properties
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 17
Contact Properties
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 18
Contact Properties
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 19
Contact Properties
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 20
Contact Properties
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 21
Contact Properties
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 22
Contact Properties
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 23
Contact Properties
contact.imageData
contact.givenName
contact.familyName
contact.emailAddresses
contact.phoneNumbers
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 24
Contact Properties
contact.imageData
contact.givenName
contact.familyName
contact.emailAddresses
contact.phoneNumbers
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 25
Contact Properties
contact.imageData
contact.givenName
contact.familyName
contact.emailAddresses
contact.phoneNumbers
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 26
Contact Properties
contact.imageData
contact.givenName
contact.familyName
contact.emailAddresses
contact.phoneNumbers
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 27
Contact Properties
contact.imageData
contact.givenName
contact.familyName
contact.emailAddresses
contact.phoneNumbers
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 28
Contact Properties
contact.imageData
contact.givenName
contact.familyName
contact.emailAddresses
contact.phoneNumbers
John
Appleseed
[email protected] (home)
[email protected] (work)
(408) 555-0126 (iPhone)
Page 30
Contact Objects
CNContact
Page 31
Contact Objects
CNContact
CNMutableContact
Page 32
Contact Objects
CNContact
CNMutableContact
CNLabeledValue
Page 33
Creating a New Contact
import Contacts
Page 34
Creating a New Contact
import Contacts // create mutable for adding to the contact let contact = CNMutableContact()
Page 35
Creating a New Contact
import Contacts // create mutable for adding to the contact let contact = CNMutableContact() contact.imageData = // profile picture as NSData
Page 36
Creating a New Contact
import Contacts // create mutable for adding to the contact let contact = CNMutableContact() contact.imageData = // profile picture as NSData
Page 37
Creating a New 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"
Page 38
Creating a New ContactLabeled Values
let homeEmail = CNLabeledValue(label: CNLabelHome, value: "[email protected] ") let workEmail = CNLabeledValue(label: CNLabelWork, value: "[email protected] ")
Page 39
Creating a New ContactLabeled Values
let homeEmail = CNLabeledValue(label: CNLabelHome, value: "[email protected] ") let workEmail = CNLabeledValue(label: CNLabelWork, value: "[email protected] ")
contact.emailAddresses = [homeEmail, workEmail]
Page 40
Creating a New ContactLabeled Values
let homeEmail = CNLabeledValue(label: CNLabelHome, value: "[email protected] ") let workEmail = CNLabeledValue(label: CNLabelWork, value: "[email protected] ")
contact.emailAddresses = [homeEmail, workEmail]
contact.phoneNumbers = [CNLabeledValue( label: CNLabelPhoneNumberiPhone, value: CNPhoneNumber(stringValue: "(408) 555-0126"))]
Page 41
Creating a New ContactLabeled Values
let address = CNMutablePostalAddress() address.street = "774 Loma Vista Ave" address.city = "Los Gatos" address.state = "CA" address.postalCode = "95032"
Page 42
Creating a New ContactLabeled Values
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)]
Page 43
Creating a New ContactDates
let birthday = NSDateComponents() birthday.day = 1 birthday.month = 4 birthday.year = 1988 // can omit for a year-less birthday
Page 44
Creating a New ContactDates
let birthday = NSDateComponents() birthday.day = 1 birthday.month = 4 birthday.year = 1988 // can omit for a year-less birthday
contact.birthday = birthday
Page 45
Formatting Contact Data
Page 46
Formatting Contact Data
let fullName = CNContactFormatter.stringFromContact(contact, style: .FullName)
Page 47
Formatting Contact Data
let fullName = CNContactFormatter.stringFromContact(contact, style: .FullName) print(fullName) // John Appleseed
Page 48
Formatting Contact Data
let fullName = CNContactFormatter.stringFromContact(contact, style: .FullName) print(fullName) // John Appleseed
let postalString = CNPostalAddressFormatter.stringFromPostalAddress(address)
Page 49
Formatting Contact Data
let fullName = CNContactFormatter.stringFromContact(contact, style: .FullName) print(fullName) // John Appleseed
let postalString = CNPostalAddressFormatter.stringFromPostalAddress(address) print(postalString) // 774 Loma Vista Ave // Los Gatos, CA 95032
Page 50
Using Contacts in Your App
Dave Dribin OS X Contacts Engineer
Page 53
Using Contacts in Your App
CNContactStore
Page 54
Fetching User's Contacts
class CNContactStore : NSObject {
func unifiedContactsMatchingPredicate(
predicate: NSPredicate,
keysToFetch: [CNKeyDescriptor]) -> [CNContact] throws
...
}
Page 55
Predicateslet predicate = CNContact.predicateForContactsMatchingName("Appleseed")
Page 56
Predicateslet predicate = CNContact.predicateForContactsMatchingName("Appleseed")
John
Appleseed
[email protected] (home)[email protected] (work)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
Jane
Appleseed
[email protected] (home)
(505) 555-0155 (home)(408) 555-0166 (work)
Craig
Bromley
[email protected] (work)
(465) 555-0199 (iPhone)
Jun 21 (birthday)
Page 57
Predicateslet predicate = CNContact.predicateForContactsMatchingName("Appleseed")
John
Appleseed
[email protected] (home)[email protected] (work)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
Jane
Appleseed
[email protected] (home)
(505) 555-0155 (home)(408) 555-0166 (work)
Craig
Bromley
[email protected] (work)
(465) 555-0199 (iPhone)
Jun 21 (birthday)
Page 58
Keys to Fetchlet keysToFetch = ["givenName", "familyName"]
imageData
givenName
familyName
emailAddresses
phoneNumbers
birthday
Jane
Appleseed
[email protected] (home)
(505) 555-0155 (home)(408) 555-0166 (work)
John
Appleseed
[email protected] (home)[email protected] (work)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
Page 59
Keys to Fetchlet keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
imageData
givenName
familyName
emailAddresses
phoneNumbers
birthday
John
Appleseed
[email protected] (home)[email protected] (work)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
Jane
Appleseed
[email protected] (home)
(505) 555-0155 (home)(408) 555-0166 (work)
Page 60
Keys to Fetchlet keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
imageData
givenName
familyName
emailAddresses
phoneNumbers
birthday
John
Appleseed
[email protected] (home)[email protected] (work)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
Jane
Appleseed
[email protected] (home)
(505) 555-0155 (home)(408) 555-0166 (work)
Page 61
How to Fetch
let predicate = CNContact.predicateForContactsMatchingName("Appleseed") let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
Page 62
How to Fetch
let predicate = CNContact.predicateForContactsMatchingName("Appleseed") let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
let store = CNContactStore() let contacts = try store.unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch)
Page 63
How to Fetch
let predicate = CNContact.predicateForContactsMatchingName("Appleseed") let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
let store = CNContactStore() let contacts = try store.unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch)
for contact in contacts { print("\(contact.givenName) \(contact.familyName)") }
Page 64
Keeping UI ResponsiveMain Queue Background Queue
Fetch Contacts
CNContactCNContactCNContact
Page 65
Keeping UI ResponsiveMain Queue Background Queue
Fetch Contacts
CNContactCNContactCNContact
CNContactCNContactCNContact
Page 67
Data Privacyclass CNContactStore : NSObject { func requestAccessForEntityType(..., completionHandler:) }
Privacy and Your App Pacific Heights Tuesday 2:30PM
Page 68
Partial Contactslet keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
John
Appleseed
[email protected] (home)[email protected] (work)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
John givenName
Appleseed familyName
[email protected] (home)[email protected] (work)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
Page 69
Partial Contactslet keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
John givenName
Appleseed familyName
phoneNumbers
[email protected] (home)[email protected] (work)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
John
Appleseed
[email protected] (home)[email protected] (work)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
Page 70
Partial Contacts
if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) { print("\(contact.phoneNumbers)") }
Page 71
Partial Contacts
if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) { print("\(contact.phoneNumbers)") } else { let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey] var refetchedContact = try store.unifiedContactWithIdentifier( contact.identifier, keysToFetch: keysToFetch) print("\(refetchedContact.phoneNumbers)") }
Page 72
Mr. namePrefix
John givenName
middleName
Appleseed familyName
Sr. nameSuffix
Formatting Partial Contactslet keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
Page 73
Mr. namePrefix
John givenName
middleName
Appleseed familyName
Sr. nameSuffix
Formatting Partial Contactslet keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey,
CNContactNamePrefixKey, CNContactMiddleNameKey, ...]
Page 74
Formatting Partial Contacts
Mr. namePrefix
John givenName
middleName
Appleseed familyName
Sr. nameSuffix
+ Any Others
let keysToFetch =
[CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName)]
Page 75
Key Descriptors
let predicate = CNContact.predicateForContactsMatchingName("Appleseed") let keysToFetch = [ CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactEmailAddressesKey]
Page 76
Key Descriptors
let predicate = CNContact.predicateForContactsMatchingName("Appleseed") let keysToFetch = [ CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactEmailAddressesKey]
let contacts = try store.unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch)
Page 77
Key Descriptors
let predicate = CNContact.predicateForContactsMatchingName("Appleseed") let keysToFetch = [ CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactEmailAddressesKey]
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)") }
Page 78
Unified Contacts
John
Appleseed
[email protected] (work)
(408) 555-0126 (iPhone)
iCloud
John
Appleseed
[email protected] (home)
April 1, 1988 (birthday)
Facebook
Page 79
John
Appleseed
[email protected] (work) [email protected] (home)
(408) 555-0126 (iPhone)
April 1, 1988 (birthday)
Unified
Unified Contacts
John
Appleseed
[email protected] (work)
(408) 555-0126 (iPhone)
iCloud
John
Appleseed
[email protected] (home)
April 1, 1988 (birthday)
Facebook
Page 80
Adding a New Contact
let john = CNMutableContact() john.givenName = "John" john.familyName = "Appleseed"
Page 81
let john = CNMutableContact() john.givenName = "John" john.familyName = "Appleseed"
let saveRequest = CNSaveRequest() saveRequest.addContact(john, toContainerWithIdentifier: nil) try store.executeSaveRequest(saveRequest)
Adding a New Contact
Page 82
Updating an Existing Contact
let updatedContact = contact.mutableCopy() let newEmail = CNLabeledValue(label: CNLabelHome, value: "[email protected] ") updatedContact.emailAddresses.append(newEmail)
Page 83
Updating an Existing 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)
Page 84
Contacts in the UI
Julien Robert iOS Contacts Engineer
Page 85
ContactsUINew framework
Page 86
ContactsUINew framework
iOS OS X
Page 87
ContactsUINew framework
iOS OS X
CNContactPickerViewController CNContactPicker
Page 88
ContactsUINew framework
iOS OS X
CNContactPickerViewController CNContactPicker
CNContactViewController CNContactViewController
Page 89
Picking ContactsCNContactPickerViewController
Page 90
Picking ContactsCNContactPickerViewController
Modern replacement for ABPeoplePickerNavigationController
Page 91
Picking ContactsCNContactPickerViewController
Modern replacement for ABPeoplePickerNavigationControllerMust be presented, not pushed
Page 92
Picking ContactsCNContactPickerViewController
Modern replacement for ABPeoplePickerNavigationControllerMust be presented, not pushedAlways out of process, no contacts access dialog
Page 93
Picking ContactsCNContactPickerViewController
Modern replacement for ABPeoplePickerNavigationControllerMust be presented, not pushedAlways out of process, no contacts access dialogMay return partial contacts
Page 94
Picking ContactsCNContactPickerViewController
Modern replacement for ABPeoplePickerNavigationControllerMust be presented, not pushedAlways out of process, no contacts access dialogMay return partial contactsBehavior based on delegate methods and predicates
Page 95
Picking ContactsCNContactPickerViewController
Modern replacement for ABPeoplePickerNavigationControllerMust be presented, not pushedAlways out of process, no contacts access dialogMay return partial contactsBehavior based on delegate methods and predicatesSupports multi-selection
Page 96
Picking ContactsDelegate methods
Page 97
Picking ContactsDelegate methods
Single contact
Page 98
Picking ContactsDelegate methods
Single contact contactPicker(picker, didSelectContact contact: CNContact)
Page 99
Picking ContactsDelegate methods
Single contact contactPicker(picker, didSelectContact contact: CNContact)
Single propertycontactPicker(picker, didSelectContactProperty property: CNContactProperty)
Page 100
Picking ContactsDelegate methods
Single contact contactPicker(picker, didSelectContact contact: CNContact)
Single propertycontactPicker(picker, didSelectContactProperty property: CNContactProperty)
class CNContactProperty { var contact: CNContact var key: NSString var value: AnyObject? var identifier: NSString? }
Page 101
Picking ContactsDelegate methods
Page 102
Picking ContactsDelegate methods
Multiple contacts
Page 103
Picking ContactsDelegate methods
Multiple contactscontactPicker(picker, didSelectContacts contacts: [CNContact])
Page 104
Picking ContactsDelegate methods
Multiple contactscontactPicker(picker, didSelectContacts contacts: [CNContact])
Multiple propertiescontactPicker(picker, didSelectContactProperties properties: [CNContactProperty])
Page 105
Picking ContactsPredicates
Page 106
Picking ContactsPredicates
predicateForEnablingContact
• Which contacts are available• Evaluated on CNContact
Page 107
Picking ContactsPredicates
predicateForEnablingContact
• Which contacts are available• Evaluated on CNContact
Page 108
Picking ContactsPredicates
predicateForEnablingContact
• Which contacts are available• Evaluated on CNContact
let predicate = NSPredicate(format: "familyName LIKE[cd] 'parker'")
Page 109
Picking ContactsPredicates
predicateForEnablingContact
• Which contacts are available• Evaluated on CNContact
let predicate = NSPredicate(format: "familyName LIKE[cd] 'parker'")
contactPicker.predicateForEnablingContact = predicate
Page 110
Picking ContactsPredicates
Page 111
Picking ContactsPredicates
predicateForSelectionOfContact
• Which contacts are returned when tapped, others push the card
Page 112
Picking ContactsPredicates
predicateForSelectionOfContact
• Which contacts are returned when tapped, others push the card
predicateForSelectionOfProperty
• Which properties are returned when tapped, others perform the default action• Evaluated on CNContactProperty
Page 113
Picking ContactsPredicates
predicateForSelectionOfContact
• Which contacts are returned when tapped, others push the card
predicateForSelectionOfProperty
• Which properties are returned when tapped, others perform the default action• Evaluated on CNContactProperty
Coherence between predicates and delegate methods
Page 114
Viewing ContactsCNContactViewController
Page 115
Viewing ContactsCNContactViewController
One class to replace• ABPersonViewController
• ABNewPersonViewController
• ABUnknownPersonViewController
Page 116
Viewing ContactsCNContactViewController
Page 117
Viewing ContactsCNContactViewController
Use appropriate creation method
Page 118
Viewing ContactsCNContactViewController
Use appropriate creation method• viewControllerForContact:
Page 119
Viewing ContactsCNContactViewController
Use appropriate creation method• viewControllerForContact:
• viewControllerForNewContact:
Page 120
Viewing ContactsCNContactViewController
Use appropriate creation method• viewControllerForContact:
• viewControllerForNewContact:
• viewControllerForUnknownContact:
Page 121
Viewing ContactsCNContactViewController
Use appropriate creation method• viewControllerForContact:
• viewControllerForNewContact:
• viewControllerForUnknownContact:
Page 122
Viewing ContactsCNContactViewController
Use appropriate creation method• viewControllerForContact:
• viewControllerForNewContact:
• viewControllerForUnknownContact:
Always out of process
Page 123
Viewing ContactsCNContactViewController
Use appropriate creation method• viewControllerForContact:
• viewControllerForNewContact:
• viewControllerForUnknownContact:
Always out of processContact must be fetched with descriptorForRequiredKeys
Page 124
Viewing ContactsExample
let contact = try contactStore.unifiedContactWithIdentifier(identifier,keysToFetch: [CNContactViewController.descriptorForRequiredKeys])
Page 125
Viewing ContactsExample
let contact = try contactStore.unifiedContactWithIdentifier(identifier,keysToFetch: [CNContactViewController.descriptorForRequiredKeys])
let viewController = CNContactViewController(forContact: contact)
Page 126
Viewing ContactsExample
let contact = try contactStore.unifiedContactWithIdentifier(identifier,keysToFetch: [CNContactViewController.descriptorForRequiredKeys])
let viewController = CNContactViewController(forContact: contact)
viewController.contactStore = self.contactStore viewController.delegate = self
Page 127
Viewing ContactsExample
let contact = try contactStore.unifiedContactWithIdentifier(identifier,keysToFetch: [CNContactViewController.descriptorForRequiredKeys])
let viewController = CNContactViewController(forContact: contact)
viewController.contactStore = self.contactStore viewController.delegate = self
self.pushViewController(viewController)
Page 128
Viewing ContactsExample
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 }
Page 129
DemoPicking and Viewing Contacts
Meow
Page 130
A new modern Contacts APICommon across all platforms Adopt now!
Summary
Page 131
More Information
DocumentationContacts Framework ReferenceContactsUI Framework Referencehttp://developer.apple.com/library
Technical SupportApple Developer Forumshttp://developer.apple.com/forums
General InquiriesPaul Marcos, App Frameworks [email protected]
Page 132
Labs
Contacts, Calendar and Reminders Lab Frameworks Lab A Thursday 4:30PM
Contacts, Calendar and Reminders Lab Frameworks Lab A Friday 9:00AM