My application contains a request for accessing to Address Book. As I press "Allow" button in the alert, the app is closing. Where may be mistake ? There is the code:
func getContactFromCNContact() -> [CNContact] {
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactGivenNameKey,
CNContactMiddleNameKey,
CNContactFamilyNameKey,
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactImageDataKey,
] as [Any]
var allContainers: [CNContainer] = []
do {
allContainers = try contactStore.containers(matching: nil)
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 results for container")
}
}
return results
}
Make sure you have usage description for key NSContactsUsageDescription in you Info.plist and it's not empty.
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 am using this code to fetch contacts from device. How to arrange array to make sections of contacts according to first letter of contact name in swift 3?
lazy var contacts: [CNContact] = {
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey] 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")
}
}
results.sort{$0.givenName < $1.givenName} // sorting array by name
return results
}()
try creating an array of [String:[CNContact]]
now run your [CNContact] through a loop and app them
let sortedContacts = [String:[CNContact]]()
for contact in contacts {
let contactPrefix = contactName(//get contact name and first character)
sortedContacts[contactPrefix].append(contact)
}
I would suggest you to create an array of CNContact arrays.
I have a custom User class which stores the phone number of the user.
class User {
let phoneNumber: String
}
How do I get the corresponding contact from the users contact book?
I tried the following but it seems like this works just for the contacts name because I'm always getting nil:
let predicate = CNContact.predicateForContactsMatchingName(userInstance.phoneNumber)
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactPhoneNumbersKey]
// Is already permitted
try! CNContactStore().unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch).first // This returns nil
I've searched in the docs but I didn't find a proper solution.
let contactStroe = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey]
contactStroe.requestAccessForEntityType(.Contacts, completionHandler: { (granted, error) -> Void in
if granted {
let predicate = CNContact.predicateForContactsInContainerWithIdentifier(contactStroe.defaultContainerIdentifier())
var contacts: [CNContact]! = []
do {
contacts = try contactStroe.unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch)// [CNContact]
}catch {
}
for contact in contacts {
var phoneStr = ""
var nameStr = ""
var number: CNPhoneNumber!
if contact.phoneNumbers.count > 0 {
number = contact.phoneNumbers[0].value as! CNPhoneNumber
phoneStr = number.stringValue.stringByReplacingOccurrencesOfString("-", withString: "")
}
nameStr = contact.familyName + contact.givenName
if !nameStr.isEmpty && !phoneStr.isEmpty {
let friend = YFriendsModel()
friend.name = nameStr
friend.phone = phoneStr
self.friendArr.append(friend)
}
}
})
this is my way, you can have a test
You can't.
This is a stupid solution as a huge workaround.
Read each contact
Normalize the phone number (not the easiest thing to do!)
Cache contacts into a [String : Contact]
Then you can lookup contacts with contacts[phone_number]?
Swift 3
A nice solution, taking care also of efficiency:
func getAllContacts() {
let status = CNContactStore.authorizationStatus(for: CNEntityType.contacts) as CNAuthorizationStatus
if status == CNAuthorizationStatus.denied {
self.showAccessContactsDeniedAlert()
return
}
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey] as [Any]
let request = CNContactFetchRequest(keysToFetch:keysToFetch as! [CNKeyDescriptor])
do {
try contactStore.enumerateContacts(with: request, usingBlock: { (contact:CNContact, stop:UnsafeMutablePointer<ObjCBool>) -> Void in
print(contact)
for email in contact.emailAddresses {
var dict = [String:String]()
dict["name"] = contact.familyName + contact.givenName
dict["email"] = email.value
self.allContacts.add(dict)
}
})
} catch {
//catch
}
}
In this case I save name and email into a dict and I add it to a class variable called allContacts.
Note that a contact can have more than one email, so I create a dict for any email address in this case
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