I'm trying to insert into var contacts: [CNContact] = []
the var store = CNContactStore() but I did not find the right code for this job, i found this function that I need to give that a name
func findContactsWithName(name: String) {
AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
if accessGranted {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
do {
let predicate: NSPredicate = CNContact.predicateForContactsMatchingName(name)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)
self.tableView.reloadData()
}
catch {
print("Unable to refetch the selected contact.")
}
})
}
})
}
I want to insert self.contacts all the records and not only one with name equal
Update
Based on comment from OP, please try the following CNContactFetchRequest-based API to retrieve all contacts without a filter. I run this on a background thread to reduce any possible issues huge numbers of contacts.
func findContactsOnBackgroundThread ( completionHandler:(contacts:[CNContact]?)->()) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),CNContactPhoneNumbersKey] //CNContactIdentifierKey
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch)
var contacts = [CNContact]()
CNContact.localizedStringForKey(CNLabelPhoneNumberiPhone)
fetchRequest.mutableObjects = false
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .UserDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContactsWithFetchRequest(fetchRequest) { (contact, stop) -> Void in
//do something with contact
if contact.phoneNumbers.count > 0 {
contacts.append(contact)
}
}
} catch let e as NSError {
print(e.localizedDescription)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completionHandler(contacts: contacts)
})
})
}
Generally speaking you would normally set a predicate to nil to retrieve all of the contacts when using CNContactFetchRequest class rather than as described in your code.
Note
If you want to use your existing API then I recommend setting the predicate to true:
NSPredicate(value: true)
This should make all contacts return. If that does not work consider switching to the CNConctactFetchRequest API to enumerate the Contacts. In that event you could then set the predicate to nil to fetch all contacts (using CNConctactFetchRequest).
This is how you might modify the existing method:
func findContacts()->[CNContact] {
AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
if accessGranted {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
do {
let predicate: NSPredicate = NSPredicate(value: true)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)
self.tableView.reloadData()
}
catch {
print("Unable to refetch the selected contact.")
}
})
}
})
}
And to use:
let contacts = findContacts()
Apple has a simpler sample:
let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName("Appleseed"), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])
For your use-case, you could try to modify the Apple Sample like this:
//Use the reference to look up additional keys constants that you may want to fetch
let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(NSPredicate(value: true), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])
More Apple Samples for the Contacts Framework
Modified Tommie C's answer for XCode 8 & Swift 3.0.
func findContactsOnBackgroundThread ( completionHandler:#escaping (_ contacts:[CNContact]?)->()) {
DispatchQueue.global(qos: .userInitiated).async(execute: { () -> Void in
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),CNContactPhoneNumbersKey] as [Any] //CNContactIdentifierKey
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch as! [CNKeyDescriptor])
var contacts = [CNContact]()
CNContact.localizedString(forKey: CNLabelPhoneNumberiPhone)
if #available(iOS 10.0, *) {
fetchRequest.mutableObjects = false
} else {
// Fallback on earlier versions
}
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .userDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContacts(with: fetchRequest) { (contact, stop) -> Void in
//do something with contact
if contact.phoneNumbers.count > 0 {
contacts.append(contact)
}
}
} catch let e as NSError {
print(e.localizedDescription)
}
DispatchQueue.main.async(execute: { () -> Void in
completionHandler(contacts)
})
})
}
override func viewDidLoad() {
findContactsOnBackgroundThread { (contacts) in
self.contactsList = contacts
self.tableView.reloadData()
}
}
/// The default key descriptors to fetch
public var defaultFetchKeys: [String] = [CNContactGivenNameKey,
CNContactFamilyNameKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey]
/**
Fetch contacts from the user's device
- parameter sortOrder: The sort order that should be used. Default none.
- returns: A list of contacts
*/
public func fetchContacts(withSortOrder sortOrder: CNContactSortOrder = .givenName,
qos: DispatchQoS.QoSClass = .background) -> Promise<[Contact]>
{
return Promise { seal in
DispatchQueue.global(qos: qos).async {
let store = CNContactStore()
var contacts = [Contact]()
/// A `PhoneNumberKit` object used to parse and format numbers (expensive!)
let phoneNumberKit = PhoneNumberKit()
let request = CNContactFetchRequest(keysToFetch: self.defaultFetchKeys as [CNKeyDescriptor])
request.sortOrder = sortOrder
request.unifyResults = true
do {
try store.enumerateContacts(with: request) { contact, _ in
if let user = Contact(contact: contact, kit: phoneNumberKit) {
contacts.append(user)
}
}
DispatchQueue.main.async {
seal.fulfill(contacts)
}
} catch {
DispatchQueue.main.async {
seal.reject(error)
}
}
}
}
}
I am also using PhoneNumberKit to standardise all numbers and PromiseKit, to chain my requests, but you can pretty much adjust it whatever you want it.
Related
My goal is to display data fetched from realm database on the table view that I previously asynchronously added whenever I launch the app.
The problem is that when I first launch my app the table view doesn't show any data fetched from realm database but if I pull it to refresh the data is displayed.
Here is what I do in the following code:
I fetch data (contacts) from apple's contacts framework and pass them through a closure to a helper method where I:
Add them to realm database
fetch the contacts and store them in the data source array (setDataSource)
create the section titles for the table view
So far I made the debugging and noticed that the problem maybe with threading and concurrency, that is, on first launch the data is being added to realm asynchronously so when I fetch the contacts there's nothing to fetch.
Saying that, I haven't been able to solve the above identified problem so far because I don't know how to. I am learning to code with threading and concurrency and to use realm.
I've also seen some posts but in none of them were a similar problem.
I'd be very grateful for your help.
Thank you!
Table view controller:
override func viewDidLoad() {
super.viewDidLoad()
populateTableView() // TODO: Contacts don't show up on first launch of the app after installation.
}
func populateTableView() {
contactManager.populateDataSource(for: self)
tableView.reloadData()
}
Contact manager Class:
let store = CNContactStore()
let dataBase = DataBase()
var contactsDataSource = [[Contact]]()
var sectionTitles = [String]()
var deletedContactsDataSource = [[Contact]]()
var sectionTitlesForDeletedContactsTable = [String]()
func fetchUserContacts(completion: #escaping ContactsFetchingResult) {
print("Attempting to fetch contacts today...")
store.requestAccess(for: .contacts) { (granted, error) in
if let errorToCatch = error {
print("Failed to request access: ", errorToCatch)
} else if granted {
print("Access granted")
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactThumbnailImageDataKey, CNContactImageDataAvailableKey, CNContactIdentifierKey, CNContactOrganizationNameKey]
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
do {
try self.store.enumerateContacts(with: request, usingBlock: { (contact, stopPointerIfYouWantToStopEnumerating) in
completion(contact, nil)
})
} catch {
completion(nil, error)
}
} else {
print("Access denied")
}
}
}
func populateDataSource(for viewController: UIViewController?) {
fetchUserContacts { (result, error) in
if let errorToCatch = error {
guard let vc = viewController else { return }
UITableViewController.Alert.showFetchingErrorAlert(on: vc, message: errorToCatch.localizedDescription)
} else if let contact = result {
self.dataBase.insert(each: Contact(contact: contact, wasDeleted: false))
self.setDataSource()
self.setSectionTitles()
}
}
}
func setDataSource(from searchTerm: String = "") {
//Not deleted contacts
if !searching {
contactsDataSource = formatResults(from: dataBase.fetchContacts(), using: sectionTitles)
} else {
setDataSourceForFilteredContacts(from: searchTerm)
}
//Deleted contacts
deletedContactsDataSource = formatResults(from: dataBase.fetchDeletedContacts(), using: sectionTitlesForDeletedContactsTable)
}
func setSectionTitles() {
sectionTitles = generateSectionTitles(from: dataBase.fetchContacts())
sectionTitlesForDeletedContactsTable = generateSectionTitles(from: dataBase.fetchDeletedContacts())
}
Database:
func insert(each contact: Contact) {
let realm = try! Realm()
try! realm.write {
realm.add(contact, update: .modified)
}
}
func fetchContacts() -> Results<Contact> {
let realm = try! Realm()
return realm.objects(Contact.self).filter("wasDeleted = false").sorted(byKeyPath: "firstName", ascending: true)
}
i have an Array contain list Contacts has been fetch.
Here is my sample code
func getData() -> [CNContact] {
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactEmailAddressesKey,
CNContactDatesKey
] as [Any]
var allContainers: [CNContainer] = []
do {
allContainers = try contactStore.containers(matching: nil)
} catch {
print("err")
}
var listContacts: [CNContact] = []
for container in allContainers {
let fetchPredecate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)
do {
let containerResult = try contactStore.unifiedContacts(matching: fetchPredecate, keysToFetch: keysToFetch as! [CNKeyDescriptor] )
listContacts.append(contentsOf: containerResult)
} catch {
}
}
// sort by name given
let result = listContacts.sorted(by: {
(firt: CNContact, second: CNContact) -> Bool in firt.givenName < second.givenName
})
return result
}
And i need to load to UITableView look like this
so, how can i do that ? Thanks you !
p/s: sorry my bad english
I have used AddessBook library (for ios8) to get all contact (firstname, lastname, contactId, all phone number). it worked perfect on simulitor.
my code:
private func getContacts()->[Person] {
var peapleOfContact: [Person] = []
if !self.determineStatus() {
return peapleOfContact
}
if let people = ABAddressBookCopyArrayOfAllPeople(self.adbk)?.takeRetainedValue() as? NSArray {
for person in people{
let contactID = ABRecordGetRecordID(person)
let firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty).takeRetainedValue() as! String
let lastName = ABRecordCopyValue(person, kABPersonLastNameProperty).takeRetainedValue() as! String
let personOfContact = Person(id: String(contactID), firstName: firstName, lastName: lastName)
let numbers:ABMultiValue = ABRecordCopyValue(person, kABPersonPhoneProperty).takeRetainedValue()
for ix in 0 ..< ABMultiValueGetCount(numbers) {
let type = ABMultiValueCopyLabelAtIndex(numbers,ix).takeRetainedValue() as String
let number = ABMultiValueCopyValueAtIndex(numbers,ix).takeRetainedValue() as! String
let cleaned = self.removeSpecialCharsFromString(type)
let shortNumber = self.makeSpecialShortNumberFromString(number)
let phone = Number(number: number, short: shortNumber, type: cleaned)
personOfContact.addPhoneNumber(phone)
}
peapleOfContact.append(personOfContact)
}
}
return peapleOfContact
}
when xcode 7.1.1 is updated and used ios9, this library not work so used ContactUI Framework.
my code:
private func getContact(){
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),
CNContactImageDataKey,
CNContactPhoneNumbersKey]
let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch)
do {
try store.enumerateContactsWithFetchRequest(fetchRequest, usingBlock: { (let contact, let stop) -> Void in
self.contacts.append(contact)
})
}
catch let error as NSError {
print(error.localizedDescription)
}
}
and used like this:
self.getContact()
for contact in self.contacts{
print("Full name: \(CNContactFormatter.stringFromContact(contact, style: .FullName))")
print("Give name: \(contact.givenName)")
print("fimily name: \(contact.familyName)")
print("Idnetifier name: \(contact.identifier)")
for phoneNo in contact.phoneNumbers {
if phoneNo.label == CNLabelPhoneNumberMobile {
let number = (phoneNo.value as! CNPhoneNumber).stringValue
print("Phone Number: \(number)")
}
}
}
but not found contactId. How to get contactId ? How to use ContactUI Framework ?
advise me. thanks
In iOS 9.*, there is a new framework called 'Contacts'
Usage:
import Contacts
var validContacts: [CNContact] = []
let contactStore = CNContactStore()
// Request for contact access
contactStore.requestAccessForEntityType(.Contacts) { (granted, e) -> Void in
if granted {
do {
// Specify the key fields that you want to be fetched.
// Note: if you didn't specify your specific field request. your app will crash
let fetchRequest = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey, CNContactMiddleNameKey, CNContactFamilyNameKey, CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactImageDataKey, CNContactThumbnailImageDataKey])
try contactStore.enumerateContactsWithFetchRequest(fetchRequest, usingBlock: { (contact, error) -> Void in
// Lets filter (optional)
if !contact.emailAddresses.isEmpty || !contact.phoneNumbers.isEmpty {
validContacts.append(contact)
}
})
print(validContacts)
}catch let e as NSError {
print(e)
}
}
}
Parsing:
// Loop through contatcs
for contact in validContacts {
var phoneNumbers: [String] = []
for phoneNumber in contact.phoneNumbers {
let value = phoneNumber.value as! CNPhoneNumber
phoneNumbers.append(value.stringValue)
}
var emailAddresses: [String] = []
for emailAddress in contact.emailAddresses {
let value = emailAddress.value as! String
emailAddresses.append(value)
}
if let imageData = contact.imageData {
let image = UIImage(data: imageData)
print("image: \(image)")
}
// Lets log
print("givenName: \(contact.givenName), middleName: \(contact.middleName), familyName: \(contact.familyName), phoneNumbers: \(phoneNumbers), emailAddresses: \(emailAddresses)\n")
}
I have a sample project which implement this.
GitHub
This code sample will help you :
public static func refreshContacts(){
let status = ABAddressBookGetAuthorizationStatus()
if status == .Denied || status == .Restricted {
// user previously denied, to tell them to fix that in settings
return
}
// open it
var error: Unmanaged<CFError>?
let addressBook: ABAddressBook? = ABAddressBookCreateWithOptions(nil, &error)?.takeRetainedValue()
if addressBook == nil {
println(error?.takeRetainedValue())
return
}
// request permission to use it
ABAddressBookRequestAccessWithCompletion(addressBook) {
granted, error in
if !granted {
// warn the user that because they just denied permission, this functionality won't work
// also let them know that they have to fix this in settings
return
}
if let people = ABAddressBookCopyArrayOfAllPeople(addressBook)?.takeRetainedValue() as? NSArray {
for person in people{
if let name = ABRecordCopyValue(person, kABPersonFirstNameProperty).takeRetainedValue() as? String {
println(name)//persons name
}
let numbers:ABMultiValue = ABRecordCopyValue(
person, kABPersonPhoneProperty).takeRetainedValue()
for ix in 0 ..< ABMultiValueGetCount(numbers) {
let label = ABMultiValueCopyLabelAtIndex(numbers,ix).takeRetainedValue() as String
let value = ABMultiValueCopyValueAtIndex(numbers,ix).takeRetainedValue() as! String
println("Phonenumber \(label) is \(value))
}
}
}
}
}
Please try this working for me. sort according to givenname(firstname). import Contacts first
let contactStore = CNContactStore()
var results: [CNContact] = []
do {
let request:CNContactFetchRequest
request = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactMiddleNameKey, CNContactEmailAddressesKey,CNContactPhoneNumbersKey])
request.sortOrder = CNContactSortOrder.GivenName
try contactStore.enumerateContactsWithFetchRequest(request) {
(contact, cursor) -> Void in
results.append(contact)
}
}
catch{
print("Handle the error please")
}
print(results)
I am aware of the ios swift has a Contacts Framework where I can fetch contacts, but I cannot find any method to fetch all the contacts together where I can access each of the contacts from that array. All methods for fetching contacts seems to require some sort of conditions. Is there any method where I can get all the contacts together?
Thanks
Swift 4 and 5. I have create class PhoneContacts. Please add NSContactsUsageDescription key to your info.plist file
import Foundation
import ContactsUI
class PhoneContacts {
class func getContacts(filter: ContactsFilter = .none) -> [CNContact] { // ContactsFilter is Enum find it below
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactPhoneNumbersKey,
CNContactEmailAddressesKey,
CNContactThumbnailImageDataKey] as [Any]
var allContainers: [CNContainer] = []
do {
allContainers = try contactStore.containers(matching: nil)
} catch {
print("Error fetching containers")
}
var results: [CNContact] = []
for container in allContainers {
let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)
do {
let containerResults = try contactStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keysToFetch as! [CNKeyDescriptor])
results.append(contentsOf: containerResults)
} catch {
print("Error fetching containers")
}
}
return results
}
}
The calling to above method in another class
import ContactsUI
func phoneNumberWithContryCode() -> [String] {
let contacts = PhoneContacts.getContacts() // here calling the getContacts methods
var arrPhoneNumbers = [String]()
for contact in contacts {
for ContctNumVar: CNLabeledValue in contact.phoneNumbers {
if let fulMobNumVar = ContctNumVar.value as? CNPhoneNumber {
//let countryCode = fulMobNumVar.value(forKey: "countryCode") get country code
if let MccNamVar = fulMobNumVar.value(forKey: "digits") as? String {
arrPhoneNumbers.append(MccNamVar)
}
}
}
}
return arrPhoneNumbers // here array has all contact numbers.
}
Now, Get email and phone of contacts
enum ContactsFilter {
case none
case mail
case message
}
var phoneContacts = [PhoneContact]() // array of PhoneContact(It is model find it below)
var filter: ContactsFilter = .none
self.loadContacts(filter: filter) // Calling loadContacts methods
fileprivate func loadContacts(filter: ContactsFilter) {
phoneContacts.removeAll()
var allContacts = [PhoneContact]()
for contact in PhoneContacts.getContacts(filter: filter) {
allContacts.append(PhoneContact(contact: contact))
}
var filterdArray = [PhoneContact]()
if self.filter == .mail {
filterdArray = allContacts.filter({ $0.email.count > 0 }) // getting all email
} else if self.filter == .message {
filterdArray = allContacts.filter({ $0.phoneNumber.count > 0 })
} else {
filterdArray = allContacts
}
phoneContacts.append(contentsOf: filterdArray)
for contact in phoneContacts {
print("Name -> \(contact.name)")
print("Email -> \(contact.email)")
print("Phone Number -> \(contact.phoneNumber)")
}
let arrayCode = self.phoneNumberWithContryCode()
for codes in arrayCode {
print(codes)
}
DispatchQueue.main.async {
self.tableView.reloadData() // update your tableView having phoneContacts array
}
}
}
PhoneContact Model Class
import Foundation
import ContactsUI
class PhoneContact: NSObject {
var name: String?
var avatarData: Data?
var phoneNumber: [String] = [String]()
var email: [String] = [String]()
var isSelected: Bool = false
var isInvited = false
init(contact: CNContact) {
name = contact.givenName + " " + contact.familyName
avatarData = contact.thumbnailImageData
for phone in contact.phoneNumbers {
phoneNumber.append(phone.value.stringValue)
}
for mail in contact.emailAddresses {
email.append(mail.value as String)
}
}
override init() {
super.init()
}
}
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")
}
Update for Swift 4
let contactStore = CNContactStore()
var contacts = [CNContact]()
let keys = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactPhoneNumbersKey,
CNContactEmailAddressesKey
] as [Any]
let request = CNContactFetchRequest(keysToFetch: keys as! [CNKeyDescriptor])
do {
try contactStore.enumerateContacts(with: request){
(contact, stop) in
// Array containing all unified contacts from everywhere
contacts.append(contact)
for phoneNumber in contact.phoneNumbers {
if let number = phoneNumber.value as? CNPhoneNumber, let label = phoneNumber.label {
let localizedLabel = CNLabeledValue<CNPhoneNumber>.localizedString(forLabel: label)
print("\(contact.givenName) \(contact.familyName) tel:\(localizedLabel) -- \(number.stringValue), email: \(contact.emailAddresses)")
}
}
}
print(contacts)
} catch {
print("unable to fetch contacts")
}
// You may add more "keys" to fetch referred to official documentation
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName)]
// The container means
// that the source the contacts from, such as Exchange and iCloud
var allContainers: [CNContainer] = []
do {
allContainers = try store.containersMatchingPredicate(nil)
} catch {
print("Error fetching containers")
}
var contacts: [CNContact] = []
// Loop the containers
for container in allContainers {
let fetchPredicate = CNContact.predicateForContactsInContainerWithIdentifier(container.identifier)
do {
let containerResults = try store.unifiedContactsMatchingPredicate(fetchPredicate, keysToFetch: keysToFetch)
// Put them into "contacts"
contacts.appendContentsOf(containerResults)
} catch {
print("Error fetching results for container")
}
}
A Swift 4.0 implementation to pull in all of the contacts.
let contactStore = CNContactStore()
var contacts = [CNContact]()
let keys = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName)]
let request = CNContactFetchRequest(keysToFetch: keys)
do {
try contactStore.enumerateContacts(with: request) { (contact, stop) in
contacts.append(contact)
}
} catch {
print(error.localizedDescription)
}
This creates a local property to store the contacts, which are then populated via the enumeration against contactStore.
Please see my answer it is based on answers above with certain improvements, just do the following
In your pod file
source 'https://github.com/CocoaPods/Specs.git'
pod 'PhoneNumberKit', '~> 2.6'
then run pod install
then in your ViewController File
import Contacts
import PhoneNumberKit
import UIKit
override func viewDidLoad() {
super.viewDidLoad()
let contactStore = CNContactStore()
var contacts = [CNContact]()
let keys = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactPhoneNumbersKey,
CNContactEmailAddressesKey,
] as [Any]
let request = CNContactFetchRequest(keysToFetch: keys as! [CNKeyDescriptor])
do {
try contactStore.enumerateContacts(with: request) {
contact, _ in
// Array containing all unified contacts from everywhere
contacts.append(contact)
for phoneNumber in contact.phoneNumbers {
if let number = phoneNumber.value as? CNPhoneNumber, let label = phoneNumber.label {
let localizedLabel = CNLabeledValue<CNPhoneNumber>.localizedString(forLabel: label)
// Get The Name
let name = contact.givenName + " " + contact.familyName
print(name)
// Get The Mobile Number
var mobile = number.stringValue
mobile = mobile.replacingOccurrences(of: " ", with: "")
// Parse The Mobile Number
let phoneNumberKit = PhoneNumberKit()
do {
let phoneNumberCustomDefaultRegion = try phoneNumberKit.parse(mobile, withRegion: "IN", ignoreType: true)
let countryCode = String(phoneNumberCustomDefaultRegion.countryCode)
let mobile = String(phoneNumberCustomDefaultRegion.nationalNumber)
let finalMobile = "+" + countryCode + mobile
print(finalMobile)
} catch {
print("Generic parser error")
}
// Get The Email
var email: String
for mail in contact.emailAddresses {
email = mail.value as String
print(email)
}
}
}
}
} catch {
print("unable to fetch contacts")
}
}
Please Try below function, it helps you (Swift 4)
import UIKit
import Contacts
import ContactsUI
override func viewDidLoad() {
super.viewDidLoad()
// `contacts` Contains all details of Phone Contacts
let contacts = self.getContactFromCNContact()
for contact in contacts {
print(contact.middleName)
print(contact.familyName)
print(contact.givenName)
}
}
func getContactFromCNContact() -> [CNContact] {
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactGivenNameKey,
CNContactMiddleNameKey,
CNContactFamilyNameKey,
CNContactEmailAddressesKey,
] as [Any]
//Get all the containers
var allContainers: [CNContainer] = []
do {
allContainers = try contactStore.containers(matching: 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.predicateForContactsInContainer(withIdentifier: container.identifier)
do {
let containerResults = try contactStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keysToFetch as! [CNKeyDescriptor])
results.append(contentsOf: containerResults)
} catch {
print("Error fetching results for container")
}
}
return results
}
Complex solution:
var contacts = [CNContact]()
let keys: [Any] = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactImageDataKey,
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactJobTitleKey,
CNContactBirthdayKey,
CNContactPostalAddressesKey
]
let request = CNContactFetchRequest(keysToFetch: keys as! [CNKeyDescriptor])
let contactStore = CNContactStore()
try? contactStore.enumerateContacts(with: request, usingBlock: { contact, _ in
contacts.append(contact)
})
Don't forget about Info.plist:
NSContactsUsageDescription
I'm trying to insert into var contacts: [CNContact] = []
the var store = CNContactStore() but I did not find the right code for this job, i found this function that I need to give that a name
func findContactsWithName(name: String) {
AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
if accessGranted {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
do {
let predicate: NSPredicate = CNContact.predicateForContactsMatchingName(name)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)
self.tableView.reloadData()
}
catch {
print("Unable to refetch the selected contact.")
}
})
}
})
}
I want to insert self.contacts all the records and not only one with name equal
Update
Based on comment from OP, please try the following CNContactFetchRequest-based API to retrieve all contacts without a filter. I run this on a background thread to reduce any possible issues huge numbers of contacts.
func findContactsOnBackgroundThread ( completionHandler:(contacts:[CNContact]?)->()) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),CNContactPhoneNumbersKey] //CNContactIdentifierKey
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch)
var contacts = [CNContact]()
CNContact.localizedStringForKey(CNLabelPhoneNumberiPhone)
fetchRequest.mutableObjects = false
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .UserDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContactsWithFetchRequest(fetchRequest) { (contact, stop) -> Void in
//do something with contact
if contact.phoneNumbers.count > 0 {
contacts.append(contact)
}
}
} catch let e as NSError {
print(e.localizedDescription)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completionHandler(contacts: contacts)
})
})
}
Generally speaking you would normally set a predicate to nil to retrieve all of the contacts when using CNContactFetchRequest class rather than as described in your code.
Note
If you want to use your existing API then I recommend setting the predicate to true:
NSPredicate(value: true)
This should make all contacts return. If that does not work consider switching to the CNConctactFetchRequest API to enumerate the Contacts. In that event you could then set the predicate to nil to fetch all contacts (using CNConctactFetchRequest).
This is how you might modify the existing method:
func findContacts()->[CNContact] {
AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
if accessGranted {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
do {
let predicate: NSPredicate = NSPredicate(value: true)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)
self.tableView.reloadData()
}
catch {
print("Unable to refetch the selected contact.")
}
})
}
})
}
And to use:
let contacts = findContacts()
Apple has a simpler sample:
let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName("Appleseed"), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])
For your use-case, you could try to modify the Apple Sample like this:
//Use the reference to look up additional keys constants that you may want to fetch
let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(NSPredicate(value: true), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])
More Apple Samples for the Contacts Framework
Modified Tommie C's answer for XCode 8 & Swift 3.0.
func findContactsOnBackgroundThread ( completionHandler:#escaping (_ contacts:[CNContact]?)->()) {
DispatchQueue.global(qos: .userInitiated).async(execute: { () -> Void in
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),CNContactPhoneNumbersKey] as [Any] //CNContactIdentifierKey
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch as! [CNKeyDescriptor])
var contacts = [CNContact]()
CNContact.localizedString(forKey: CNLabelPhoneNumberiPhone)
if #available(iOS 10.0, *) {
fetchRequest.mutableObjects = false
} else {
// Fallback on earlier versions
}
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .userDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContacts(with: fetchRequest) { (contact, stop) -> Void in
//do something with contact
if contact.phoneNumbers.count > 0 {
contacts.append(contact)
}
}
} catch let e as NSError {
print(e.localizedDescription)
}
DispatchQueue.main.async(execute: { () -> Void in
completionHandler(contacts)
})
})
}
override func viewDidLoad() {
findContactsOnBackgroundThread { (contacts) in
self.contactsList = contacts
self.tableView.reloadData()
}
}
/// The default key descriptors to fetch
public var defaultFetchKeys: [String] = [CNContactGivenNameKey,
CNContactFamilyNameKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey]
/**
Fetch contacts from the user's device
- parameter sortOrder: The sort order that should be used. Default none.
- returns: A list of contacts
*/
public func fetchContacts(withSortOrder sortOrder: CNContactSortOrder = .givenName,
qos: DispatchQoS.QoSClass = .background) -> Promise<[Contact]>
{
return Promise { seal in
DispatchQueue.global(qos: qos).async {
let store = CNContactStore()
var contacts = [Contact]()
/// A `PhoneNumberKit` object used to parse and format numbers (expensive!)
let phoneNumberKit = PhoneNumberKit()
let request = CNContactFetchRequest(keysToFetch: self.defaultFetchKeys as [CNKeyDescriptor])
request.sortOrder = sortOrder
request.unifyResults = true
do {
try store.enumerateContacts(with: request) { contact, _ in
if let user = Contact(contact: contact, kit: phoneNumberKit) {
contacts.append(user)
}
}
DispatchQueue.main.async {
seal.fulfill(contacts)
}
} catch {
DispatchQueue.main.async {
seal.reject(error)
}
}
}
}
}
I am also using PhoneNumberKit to standardise all numbers and PromiseKit, to chain my requests, but you can pretty much adjust it whatever you want it.