Solución:
El problema con su implementación es que accede a la libreta de direcciones en cada búsqueda que realiza.
Si, en cambio, mantiene en memoria el contenido de la libreta de direcciones después del primer acceso, no alcanzará este alto uso de CPU.
-
Primero mantenga una var perezosa en su controlador que contendrá el contenido de la libreta de direcciones:
lazy var contacts: [CNContact] = let contactStore = CNContactStore() let keysToFetch = [ CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactImageDataAvailableKey, CNContactThumbnailImageDataKey] // Get all the containers var allContainers: [CNContainer] = [] do allContainers = try contactStore.containersMatchingPredicate(nil) catch print("Error fetching containers") var results: [CNContact] = [] // Iterate all containers and append their contacts to our results array for container in allContainers let fetchPredicate = CNContact.predicateForContactsInContainerWithIdentifier(container.identifier) do let containerResults = try contactStore.unifiedContactsMatchingPredicate(fetchPredicate, keysToFetch: keysToFetch) results.appendContentsOf(containerResults) catch print("Error fetching results for container") return results ()
- Iterar a través de la memoria array cuando busca un contacto con un número de teléfono específico:
.
func searchForContactUsingPhoneNumber(phoneNumber: String) -> [CNContact] var result: [CNContact] = [] for contact in self.contacts if (!contact.phoneNumbers.isEmpty) let phoneNumberToCompareAgainst = phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("") for phoneNumber in contact.phoneNumbers if let phoneNumberStruct = phoneNumber.value as? CNPhoneNumber let phoneNumberString = phoneNumberStruct.stringValue let phoneNumberToCompare = phoneNumberString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("") if phoneNumberToCompare == phoneNumberToCompareAgainst result.append(contact) return result
Lo probé con una libreta de direcciones muy grande, funciona sin problemas.
Aquí está todo el controlador de vista parcheado como referencia.
import UIKit
import Contacts
class ViewController: UIViewController
lazy var contacts: [CNContact] =
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey]
// Get all the containers
var allContainers: [CNContainer] = []
do
allContainers = try contactStore.containersMatchingPredicate(nil)
catch
print("Error fetching containers")
var results: [CNContact] = []
// Iterate all containers and append their contacts to our results array
for container in allContainers
let fetchPredicate = CNContact.predicateForContactsInContainerWithIdentifier(container.identifier)
do
let containerResults = try contactStore.unifiedContactsMatchingPredicate(fetchPredicate, keysToFetch: keysToFetch)
results.appendContentsOf(containerResults)
catch
print("Error fetching results for container")
return results
()
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let contact = searchForContactUsingPhoneNumber("(555)564-8583")
print(contact)
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
func searchForContactUsingPhoneNumber(phoneNumber: String) -> [CNContact]
var result: [CNContact] = []
for contact in self.contacts
if (!contact.phoneNumbers.isEmpty)
let phoneNumberToCompareAgainst = phoneNumber.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
for phoneNumber in contact.phoneNumbers
if let phoneNumberStruct = phoneNumber.value as? CNPhoneNumber
let phoneNumberString = phoneNumberStruct.stringValue
let phoneNumberToCompare = phoneNumberString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
if phoneNumberToCompare == phoneNumberToCompareAgainst
result.append(contact)
return result
Usé la respuesta de flohei para la parte de lazy var.
ACTUALIZACIÓN DE SWIFT 4
1) Agregar a .plist
NSContactsUsageDescription
Our application needs to your contacts
2) Solicite autorización si no la tiene
func requestAccess()
let store = CNContactStore()
store.requestAccess(for: .contacts) granted, error in
guard granted else
DispatchQueue.main.async
self.presentSettingsActionSheet()
return
func presentSettingsActionSheet()
let alert = UIAlertController(title: "Permission to Contacts", message: "This app needs access to contacts in order to ...", preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Go to Settings", style: .default) _ in
let url = URL(string: UIApplicationOpenSettingsURLString)!
UIApplication.shared.open(url)
)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
present(alert, animated: true)
2) Verifique el estado de la autorización si lo solicita antes
if CNContactStore.authorizationStatus(for: .contacts) == .authorized
getContacts()
3) Llamar a Obtener contactos
var contacts = [CNContact]()
func getContacts()
let contactStore = CNContactStore()
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactImageDataAvailableKey, CNContactThumbnailImageDataKey]
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
request.sortOrder = CNContactSortOrder.givenName
do
try contactStore.enumerateContacts(with: request)
(contact, stop) in
self.contacts.append(contact)
catch
print("unable to fetch contacts")
4) ESTA ES LA FUNCIÓN PARA OBTENER EL NOMBRE DE CONTACTO O POR NÚMERO
func getNameFromContacts(number: String) -> String
var contactFetched : CNContact
var contactName = ""
if contacts.count > 0
let numberToBeCompared = number.components(separatedBy:CharacterSet.decimalDigits.inverted).joined(separator: "")
for c in contacts
for n in c.phoneNumbers
if let numberRetrived = n.value as? CNPhoneNumber
let numberRetrivedFixed = numberRetrived.stringValue.components(separatedBy:CharacterSet.decimalDigits.inverted).joined(separator: "")
if numberRetrivedFixed.elementsEqual(numberToBeCompared)
contactName = c.givenName
// OR get the contact --> c
contactFetched = c
return contactName
else
return ""