Fetching All Contacts in iOS Swift

Fetching all contacts in ios Swift?

Many answers to Contact Framework questions suggest iterating over various containers (accounts). However, Apple's documentation describes a "Unified Contact" as

Contacts in different accounts that represent the same person may be automatically linked together. Linked contacts are displayed in OS X 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.
Source

So simplest way to fetch a list of (partial, based on keys) contacts in a single array, would be the following:

      var contacts = [CNContact]()
let keys = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName)]
let request = CNContactFetchRequest(keysToFetch: keys)

let contactStore = CNContactStore()
do {
try contactStore.enumerateContacts(with: request) {
(contact, stop) in
// Array containing all unified contacts from everywhere
contacts.append(contact)
}
}
catch {
print("unable to fetch contacts")
}

get all contact phone numbers with ContactsUI in swift

Instead of having it as a String, use an array of strings.

class ContactsModel : NSObject {
let givenName: String
let familyName: String
let phoneNumber: [String]
let emailAddress: String
var identifier: String
var image: UIImage

init(givenName:String, familyName:String, phoneNumber:[String], emailAddress:String, identifier:String, image:UIImage) {
self.givenName = givenName
self.familyName = familyName
self.phoneNumber = phoneNumber
self.emailAddress = emailAddress
self.identifier = identifier
self.image = image
}

class func generateModelArray() -> [ContactsModel]{
let contactStore = CNContactStore()
var contactsData = [ContactsModel]()
let key = [CNContactGivenNameKey,CNContactFamilyNameKey,CNContactImageDataKey,CNContactThumbnailImageDataKey,CNContactPhoneNumbersKey,CNContactEmailAddressesKey] as [CNKeyDescriptor]
let request = CNContactFetchRequest(keysToFetch: key)
try? contactStore.enumerateContacts(with: request, usingBlock: { (contact, stoppingPointer) in
let givenName = contact.givenName
let familyName = contact.familyName
let emailAddress = contact.emailAddresses.first?.value ?? ""
let phoneNumber: [String] = contact.phoneNumbers.map{ $0.value.stringValue }
let identifier = contact.identifier
var image = UIImage()
if contact.thumbnailImageData != nil{
image = UIImage(data: contact.thumbnailImageData!)!

}else if contact.thumbnailImageData == nil ,givenName.isEmpty || familyName.isEmpty{
image = UIImage(named: "usertwo")!
}
contactsData.append(ContactsModel(givenName: givenName, familyName: familyName, phoneNumber: phoneNumber, emailAddress: emailAddress as String, identifier: identifier, image: image))
})
return contactsData
}
}

How to fetch contact list from iPhone in swift 4?

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
}()

How to fetch all contacts record in iOS 9 using Contacts Framework

Both other answers do only load contacts from the container with the defaultContainerIdentifier. In a scenario, where the user has more than one container (i.e. an Exchange and an iCloud account which both are used to store contacts), this would only load the contacts from the account that is configured as the default. Therefore, it would not load all contacts as requested by the author of the question.

What you'll probably want to do instead is getting all the containers and iterate over them to extract all contacts from each of them. The following code snippet is an example of how we do it in one of our apps (in Swift):

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
}()

iOS Contacts How to Fetch contact by phone Number

The problem with your implementation is that you access the address book in every search you are making.

If instead you will hold in-memory the address book content after the first access you will not reach this high CPU usage.

  1. First hold a lazy var in your controller that will hold the address book content:

    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
    }()

    1. Iterate through the in-memory array when you are looking for a contact with a specific phone number:

    .

       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
    }

I tested it with a very big address book, it works smoothly.

Here is the entire view controller patched together for reference.

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

I used flohei's answer for the lazy var part.

Get a list of all contacts on iOS

Perhaps ABPerson function ABAddressBookCopyArrayOfAllPeople might do?

Example:

ABAddressBookRef addressBook = ABAddressBookCreate( );
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
CFIndex nPeople = ABAddressBookGetPersonCount( addressBook );

for ( int i = 0; i < nPeople; i++ )
{
ABRecordRef ref = CFArrayGetValueAtIndex( allPeople, i );
...
}


Related Topics



Leave a reply



Submit