Most part of AddressBook framework is deprecated in iOS 9. In the new Contacts Framework documentation only shows how to fetch records matches a NSPredicate
, bu
Apple actually recommends enumerateContactsWithFetchRequest of CNContactStore to fetch all contacts and NOT unifiedContactsMatchingPredicate.
Below is the working code for Obj-C.
CNContactStore *store = [[CNContactStore alloc] init];
//keys with fetching properties
NSArray *keys = @[CNContactGivenNameKey, CNContactPhoneNumbersKey];
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys];
NSError *error;
[store enumerateContactsWithFetchRequest:request error:&error usingBlock:^(CNContact * __nonnull contact, BOOL * __nonnull stop) {
// access it this way -> contact.givenName; etc
}];
Here is the link where apple recommends enumerate function: https://developer.apple.com/reference/contacts/cncontactstore/1403266-unifiedcontactsmatchingpredicate?language=objc#discussion
If link expired, here is what Apple wrote:
If no matches are found, this method returns an empty array (or nil in case of error). Use only the predicates from the CNContact class predicates. Compound predicates are not supported by this method. Due to unification, the returned contacts may have different identifiers than you specify. To fetch all contacts, use
enumerateContactsWithFetchRequest:error:usingBlock:
.
I am trying this code it works fine. I can fetch all contacts details using this code in swift3 latest framework using contacts:
let requestForContacts = CNContactFetchRequest(keysToFetch: [CNContactIdentifierKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: CNContactFormatterStyle.fullName), CNContactPhoneNumbersKey as CNKeyDescriptor ,CNContactImageDataKey as CNKeyDescriptor,CNContactEmailAddressesKey as CNKeyDescriptor,CNContactBirthdayKey as CNKeyDescriptor])
do {
try self.store.enumerateContacts(with: requestForContacts) { contact, stop in
print("contact:\(contact)")
self.contacts.append(contact)
}
} catch {
print(error)
}
for contact in self.contacts {
print(contact)
let firstName = contact.givenName
nameArray.append(firstName)
print("first:\(firstName)")
let phoneNumber = (contact.phoneNumbers[0].value).value(forKey: "digits")
phoneNumberArray.append(phoneNumber as! String)
let emailAddress = contact.emailAddresses[0].value(forKey: "value")
emailAddressArray.append(emailAddress as! String)
}
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
}()
For Swift 4
var results: [CNContact] = []
let fetchRequest = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactMiddleNameKey as CNKeyDescriptor, CNContactEmailAddressesKey as CNKeyDescriptor,CNContactPhoneNumbersKey as CNKeyDescriptor])
fetchRequest.sortOrder = CNContactSortOrder.userDefault
let store = CNContactStore()
do {
try store.enumerateContacts(with: fetchRequest, usingBlock: { (contact, stop) -> Void in
print(contact.phoneNumbers.first?.value ?? "no")
results.append(contact)
})
}
catch let error as NSError {
print(error.localizedDescription)
}
Older version for swift var results contains all contact
let contactStore = CNContactStore()
var results: [CNContact] = []
do {
try contactStore.enumerateContactsWithFetchRequest(CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactMiddleNameKey, CNContactEmailAddressesKey,CNContactPhoneNumbersKey])) {
(contact, cursor) -> Void in
results.append(contact)
}
}
catch{
print("Handle the error please")
}
Cody's response in Swift 3:
import Contacts
Then within whatever function you're using:
let store = CNContactStore()
store.requestAccess(for: .contacts, completionHandler: {
granted, error in
guard granted else {
let alert = UIAlertController(title: "Can't access contact", message: "Please go to Settings -> MyApp to enable contact permission", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactPhoneNumbersKey] as [Any]
let request = CNContactFetchRequest(keysToFetch: keysToFetch as! [CNKeyDescriptor])
var cnContacts = [CNContact]()
do {
try store.enumerateContacts(with: request){
(contact, cursor) -> Void in
cnContacts.append(contact)
}
} catch let error {
NSLog("Fetch contact error: \(error)")
}
print(">>>> Contact list:")
for contact in cnContacts {
let fullName = CNContactFormatter.string(from: contact, style: .fullName) ?? "No Name"
print("\(fullName): \(contact.phoneNumbers.description)")
}
})
In swift 3 and Xcode 8
you can get all contacts list
let keys = [CNContactGivenNameKey ,CNContactImageDataKey,CNContactPhoneNumbersKey]
var message: String!
//let request=CNContactFetchRequest(keysToFetch: keys)
let contactsStore = AppDelegate.AppDel.contactStore
// Get all the containers
var allContainers: [CNContainer] = []
do {
allContainers = try contactsStore.containers(matching: nil)
} catch {
print("Error fetching containers")
}
// Iterate all containers and append their contacts to our results array
for container in allContainers {
let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)
do {
let containerResults = try contactsStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keys as [CNKeyDescriptor])
self.results.append(contentsOf: containerResults)
self.tableView.reloadData()
message="\(self.results.count)"
} catch {
print("Error fetching results for container")
}
}