ManagingContacts/ManagingContacts/MGCContactStoreUtilities.swift
/* |
Copyright (C) 2017 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
A utility class used to filter contacts and containers. It creates a |
new mutable contact with the following properties: contactType, |
familyName, givenName, organizationName, urlAddresses, |
socialProfiles, instantMessageAddresses, and dates. |
It shows how to use CNContact's isKeyAvailable. It also shows how |
to retrieve and update the following properties of a contact: |
familyName, givenName,organizationName, emailAddresses, phoneNumbers, |
and postalAddresses. |
It uses the thumbnailImageData and imageData properties to fetch a |
contact profile picture's thumbnail and update its profile picture, |
respectively. |
*/ |
import UIKit |
import Contacts |
class MGCContactStoreUtilities { |
// MARK: - Initialization |
init() { |
} |
// MARK: - Create Mutable Contact |
/** |
- returns: Create and return a mutable contact with the following |
properties if available: contact type, given name, family name, |
organization, url, social profile, instant message, and anniversary. |
*/ |
func create(_ contact: MGCContact) -> CNMutableContact { |
let newContact = CNMutableContact() |
// Set contactType to Person if contact is a person and to Organization, otherwise. |
newContact.contactType = (contact.isPerson) ? .person : .organization |
newContact.familyName = contact.lastName |
newContact.givenName = contact.firstName |
newContact.organizationName = contact.organization |
if let profilePicture = contact.profilePicture, let imageData = profilePicture.imageData { |
newContact.imageData = imageData as Data |
} |
if let url = contact.url, !(url.value.isEmpty) { |
/* If the user did not enter a label for the url, set it to the |
default one, which is homepage. |
*/ |
let label = (url.label.isEmpty) ? MGCAppConfiguration.ContactInfo.homepage : url.label |
newContact.urlAddresses = [CNLabeledValue(label: label, value: NSString(string: url.value))] |
} |
if let socialProfile = contact.socialProfile, socialProfile.hasLabelValue() { |
// Fetch the social profile service associated with the user's input. |
let service = CNSocialProfile.socialProfileServiceMatching(socialProfile.label) |
// If the service exists, set the socialProfiles property. |
if let service = service { |
newContact.socialProfiles = [CNLabeledValue(label: socialProfile.label, value: CNSocialProfile(urlString: nil, |
username: socialProfile.value, |
userIdentifier: nil, |
service: service))] |
} |
} |
if let instantMessage = contact.instantMessage, instantMessage.hasLabelValue() { |
// Fetch the instant message service with the user's input. |
let service = CNInstantMessageAddress.instantMessageServiceMatching(instantMessage.label) |
// If the service exists, set the instantMessageAddresses property. |
if let service = service { |
newContact.instantMessageAddresses = [CNLabeledValue(label: instantMessage.label, value: CNInstantMessageAddress(username: instantMessage.value, service: service))] |
} |
} |
if let anniversary = contact.anniversary { |
let anniversaryDate = CNLabeledValue(label: CNLabelDateAnniversary, value: anniversary) |
newContact.dates = [anniversaryDate] |
} |
return newContact |
} |
// MARK: - Parsing Contact |
/** |
- returns: A mutable postal address object with the street, city, state, |
postal code, and country properties. |
*/ |
fileprivate func convert(_ address: MGCPostalAddress) -> CNPostalAddress { |
let newAddress = CNMutablePostalAddress() |
newAddress.street = address.street |
newAddress.city = address.city |
newAddress.state = address.state |
newAddress.postalCode = address.postalCode |
newAddress.country = address.country |
return newAddress as CNPostalAddress |
} |
/// - returns: Label and value of the first postal address found in contact. |
fileprivate func postalAddress(from contact: CNContact) -> MGCLabelValue? { |
if !contact.postalAddresses.isEmpty { |
// Only use the first address found. |
let addresses = contact.postalAddresses.first! |
let address = addresses.value |
let label = (addresses.label == nil) ? MGCAppConfiguration.ContactInfo.defaultLabel : CNLabeledValue<CNPostalAddress>.localizedString(forLabel: addresses.label!) |
let value = MGCPostalAddress(street: address.street, city: address.city, state: address.state, postalCode: address.postalCode, country: address.country) |
return MGCLabelValue(label: label, address: value) |
} |
return nil |
} |
/// - returns: Label and value of the first email address found in contact. |
fileprivate func emailLabelValue(from contact: CNContact) -> MGCLabelValue? { |
if !contact.emailAddresses.isEmpty { |
// Only use the first email address. |
let email = contact.emailAddresses.first! |
let label = (email.label == nil) ? MGCAppConfiguration.ContactInfo.defaultLabel : CNLabeledValue<NSString>.localizedString(forLabel: email.label!) |
let value = email.value as String |
return MGCLabelValue(label: label, value: value) |
} |
return nil |
} |
/// - returns: Label and value of the first phone number found in contact. |
fileprivate func phoneNumberLabelValue(from contact: CNContact) -> MGCLabelValue? { |
if !contact.phoneNumbers.isEmpty { |
// Only use the first phone number found. |
let phoneNumber = contact.phoneNumbers.first! |
/* Fetch the label associated with the phone number. The property |
label can be nil, let's check it before using it. |
*/ |
let label = (phoneNumber.label == nil) ? MGCAppConfiguration.ContactInfo.defaultLabel : CNLabeledValue<CNPhoneNumber>.localizedString(forLabel: phoneNumber.label!) |
// Get the phone number's value. |
let value = (phoneNumber.value ).stringValue |
return MGCLabelValue(label: label, value: value) |
} |
return nil |
} |
/// - returns: A model object representing a CNContact object. |
func parse(_ contact: CNContact) -> MGCContact { |
let result = MGCContact() |
/* We first check whether a property value was fetched before using it. |
Accessing a unfetched property will throw |
CNContactPropertyNotFetchedExceptionName. |
*/ |
if contact.isKeyAvailable(CNContactThumbnailImageDataKey) { |
if let imageData = contact.thumbnailImageData { |
result.profilePicture = MGCLabelValue(imageData: imageData) |
} |
} |
if contact.isKeyAvailable(CNContactGivenNameKey) { |
if !contact.givenName.isEmpty { |
result.firstName = contact.givenName |
} |
} |
if contact.isKeyAvailable(CNContactFamilyNameKey) { |
if !contact.familyName.isEmpty { |
result.lastName = contact.familyName |
} |
} |
if contact.isKeyAvailable(CNContactOrganizationNameKey) { |
if !contact.organizationName.isEmpty { |
result.organization = contact.organizationName |
} |
} |
if contact.isKeyAvailable(CNContactPhoneNumbersKey) { |
/* If the user did not provide a label for the phone number, use the |
default label, which is "Other." |
*/ |
if let number = phoneNumberLabelValue(from: contact) { |
result.phoneNumber = number |
} |
else { |
result.phoneNumber = MGCLabelValue(label: MGCAppConfiguration.ContactInfo.defaultLabel) |
} |
} |
if contact.isKeyAvailable(CNContactEmailAddressesKey) { |
/* If the user did not provide a label for the email, use the |
default label, which is "Other." |
*/ |
if let email = emailLabelValue(from: contact) { |
result.email = email |
} |
else { |
result.email = MGCLabelValue(label: MGCAppConfiguration.ContactInfo.defaultLabel) |
} |
} |
if contact.isKeyAvailable(CNContactPostalAddressesKey) { |
if let address = postalAddress(from: contact) { |
result.postalAddress = address |
} |
} |
return result |
} |
// MARK: - Filtering Content |
/// - returns: An array of contacts filtered according to a specified contact type. |
func filter(_ contacts: [CNContact], for type: CNContactType) -> [CNContact] { |
return contacts.filter({(contact: CNContact) in contact.contactType == type}) |
} |
/// - returns: An array of containers filtered according to a specified container type. |
func filter(_ containers: [CNContainer], for type: CNContainerType) -> [CNContainer] { |
return containers.filter({(container: CNContainer) in container.type == type}) |
} |
// MARK: - Update Contact |
/// - returns: Update and return an existing contact. |
func update(_ contact: CNMutableContact, with data: MGCContact) -> CNMutableContact { |
contact.givenName = data.firstName |
contact.familyName = data.lastName |
contact.organizationName = data.organization |
if let profilePicture = data.profilePicture, let imageData = profilePicture.imageData { |
contact.imageData = imageData as Data |
} |
/* First check whether the user's provided label already exists among |
the current labeled email addresses. |
If the label already exists, update its associated value. |
If it does not, create and add a new CNLabeledValue object, then add |
it to emailAddresses. We repeat the same steps for phone numbers and |
postal addresses. |
*/ |
if let email = data.email { |
let labels = contact.emailAddresses.map({(item: CNLabeledValue<NSString>) -> String in |
/* If the label property does not exist, return the default |
label and its localized string otherwise. |
*/ |
return (item.label == nil) ? MGCAppConfiguration.ContactInfo.defaultLabel : CNLabeledValue<NSString>.localizedString(forLabel: item.label!)}) |
if labels.contains(email.label) { |
/* If the label exists, then it is associated with the first |
item in the array. We only show the first item in the |
labeledValue object to the user. |
*/ |
contact.emailAddresses[0] = CNLabeledValue(label: email.label, value: NSString(string: email.value)) |
} |
else { |
contact.emailAddresses.append(CNLabeledValue(label: MGCAppConfiguration.ContactInfo.defaultLabel, value: NSString(string: email.value))) |
} |
} |
if let phoneNumber = data.phoneNumber { |
let labels = contact.phoneNumbers.map({(item: CNLabeledValue<CNPhoneNumber>) -> String in |
return (item.label == nil) ? MGCAppConfiguration.ContactInfo.defaultLabel : CNLabeledValue<CNPhoneNumber>.localizedString(forLabel: item.label!)}) |
if labels.contains(phoneNumber.label) { |
contact.phoneNumbers[0] = CNLabeledValue(label: phoneNumber.label, value: CNPhoneNumber(stringValue: phoneNumber.value)) |
} |
else { |
contact.phoneNumbers.append(CNLabeledValue(label: MGCAppConfiguration.ContactInfo.defaultLabel, value: CNPhoneNumber(stringValue: phoneNumber.value))) |
} |
} |
if let postalAddress = data.postalAddress, let address = postalAddress.address { |
let labels = contact.postalAddresses.map({(item: CNLabeledValue<CNPostalAddress>) -> String in |
return (item.label == nil) ? MGCAppConfiguration.ContactInfo.defaultLabel : CNLabeledValue<CNPostalAddress>.localizedString(forLabel: item.label!)}) |
if labels.contains(postalAddress.label) { |
contact.postalAddresses[0] = CNLabeledValue(label: postalAddress.label, value: convert(address)) |
} |
else { |
contact.postalAddresses.append(CNLabeledValue(label: MGCAppConfiguration.ContactInfo.defaultLabel, value: convert(address))) |
} |
} |
return contact |
} |
} |
Copyright © 2017 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2017-02-11