iOS 联系人 如何通过 phone 号码获取联系人
iOS Contacts How to Fetch contact by phone Number
我只想通过 phone 号码获取联系人姓名和姓氏。我试过了,但这太慢了,cpu 超过了 %120。
let contactStore = CNContactStore()
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]
var contacts = [CNContact]()
do {
try contactStore.enumerateContactsWithFetchRequest(CNContactFetchRequest.init(keysToFetch: keys), usingBlock: { (contact, cursor) in
if (!contact.phoneNumbers.isEmpty) {
for phoneNumber in contact.phoneNumbers {
if let phoneNumberStruct = phoneNumber.value as? CNPhoneNumber {
do {
let libPhone = try util.parseWithPhoneCarrierRegion(phoneNumberStruct.stringValue)
let phoneToCompare = try util.getNationalSignificantNumber(libPhone)
if formattedPhone == phoneToCompare {
contacts.append(contact)
}
}catch {
print(error)
}
}
}
}
})
if contacts.count > 0 {
contactName = (contacts.first?.givenName)! + " " + (contacts.first?.familyName)!
print(contactName)
completionHandler(contactName)
}
}catch {
print(error)
}
此外,当我使用 phonenumber Kit 查找联系人时,它会增加 cpu 并给出延迟响应。
var result: [CNContact] = []
let nationalNumber = PhoneNumberKit().parseMultiple([phoneNumber])
let number = nationalNumber.first?.toNational()
print(number)
for contact in self.addressContacts {
if (!contact.phoneNumbers.isEmpty) {
let phoneNumberToCompareAgainst = number!.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
for phoneNumber in contact.phoneNumbers {
if let phoneNumberStruct = phoneNumber.value as? CNPhoneNumber {
let phoneNumberString = phoneNumberStruct.stringValue
let nationalContactNumber = PhoneNumberKit().parseMultiple([phoneNumberString])
let nationalContactNumberString = nationalContactNumber.first?.toNational()
if nationalContactNumberString == number {
result.append(contact)
}
}
}
}
}
return result
您实施的问题是您在每次搜索时都访问地址簿。
如果您在第一次访问后将地址簿内容保存在内存中,您将不会达到这么高的 CPU 使用率。
首先在你的控制器中保存一个惰性变量来保存地址簿内容:
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
}()
- 当您查找具有特定 phone 号码的联系人时,遍历内存数组:
.
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
}
我用一个很大的通讯录测试过,很流畅
这里是拼凑起来的整个view controller,供参考。
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
}
}
我使用 作为惰性变量部分。
SWIFT 4 更新
1) 添加到.plist
<key>NSContactsUsageDescription</key>
<string>Our application needs to your contacts</string>
2) 如果您没有,请请求授权
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) 如果您之前要求,请检查授权状态
if CNContactStore.authorizationStatus(for: .contacts) == .authorized {
getContacts()
}
3) 调用获取联系人
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) 这是获取联系人姓名或号码的功能
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 ""
}
}
必须为 Swift 5 进行这些更改:
lazy var contacts = {
let contactStore = CNContactStore()
let keysToFetch: [CNKeyDescriptor] = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactEmailAddressesKey as CNKeyDescriptor,
CNContactPhoneNumbersKey as CNKeyDescriptor,
CNContactImageDataAvailableKey as CNKeyDescriptor,
CNContactThumbnailImageDataKey as CNKeyDescriptor]
// 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)
results.append(contentsOf: containerResults)
} catch {
print("Error fetching results for container")
}
}
return results
}()
对于迭代和发现:
func searchForContactUsingPhoneNumber(phoneNumber: String) -> [CNContact] {
var result: [CNContact] = []
for contact in contacts {
if (!contact.phoneNumbers.isEmpty) {
let phoneNumberToCompareAgainst = phoneNumber.components(separatedBy: NSCharacterSet.decimalDigits.inverted).joined(separator: "")
for phoneNumber in contact.phoneNumbers {
if let phoneNumberStruct = phoneNumber.value as? CNPhoneNumber {
let phoneNumberString = phoneNumberStruct.stringValue
let phoneNumberToCompare = phoneNumberString.components(separatedBy: NSCharacterSet.decimalDigits.inverted).joined(separator: "")
if phoneNumberToCompare == phoneNumberToCompareAgainst {
result.append(contact)
}
}
}
}
}
return result
}
我只想通过 phone 号码获取联系人姓名和姓氏。我试过了,但这太慢了,cpu 超过了 %120。
let contactStore = CNContactStore()
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]
var contacts = [CNContact]()
do {
try contactStore.enumerateContactsWithFetchRequest(CNContactFetchRequest.init(keysToFetch: keys), usingBlock: { (contact, cursor) in
if (!contact.phoneNumbers.isEmpty) {
for phoneNumber in contact.phoneNumbers {
if let phoneNumberStruct = phoneNumber.value as? CNPhoneNumber {
do {
let libPhone = try util.parseWithPhoneCarrierRegion(phoneNumberStruct.stringValue)
let phoneToCompare = try util.getNationalSignificantNumber(libPhone)
if formattedPhone == phoneToCompare {
contacts.append(contact)
}
}catch {
print(error)
}
}
}
}
})
if contacts.count > 0 {
contactName = (contacts.first?.givenName)! + " " + (contacts.first?.familyName)!
print(contactName)
completionHandler(contactName)
}
}catch {
print(error)
}
此外,当我使用 phonenumber Kit 查找联系人时,它会增加 cpu 并给出延迟响应。
var result: [CNContact] = []
let nationalNumber = PhoneNumberKit().parseMultiple([phoneNumber])
let number = nationalNumber.first?.toNational()
print(number)
for contact in self.addressContacts {
if (!contact.phoneNumbers.isEmpty) {
let phoneNumberToCompareAgainst = number!.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
for phoneNumber in contact.phoneNumbers {
if let phoneNumberStruct = phoneNumber.value as? CNPhoneNumber {
let phoneNumberString = phoneNumberStruct.stringValue
let nationalContactNumber = PhoneNumberKit().parseMultiple([phoneNumberString])
let nationalContactNumberString = nationalContactNumber.first?.toNational()
if nationalContactNumberString == number {
result.append(contact)
}
}
}
}
}
return result
您实施的问题是您在每次搜索时都访问地址簿。
如果您在第一次访问后将地址簿内容保存在内存中,您将不会达到这么高的 CPU 使用率。
首先在你的控制器中保存一个惰性变量来保存地址簿内容:
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 }()
- 当您查找具有特定 phone 号码的联系人时,遍历内存数组:
.
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 }
我用一个很大的通讯录测试过,很流畅
这里是拼凑起来的整个view controller,供参考。
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
}
}
我使用
SWIFT 4 更新
1) 添加到.plist
<key>NSContactsUsageDescription</key>
<string>Our application needs to your contacts</string>
2) 如果您没有,请请求授权
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) 如果您之前要求,请检查授权状态
if CNContactStore.authorizationStatus(for: .contacts) == .authorized {
getContacts()
}
3) 调用获取联系人
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) 这是获取联系人姓名或号码的功能
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 ""
}
}
必须为 Swift 5 进行这些更改:
lazy var contacts = {
let contactStore = CNContactStore()
let keysToFetch: [CNKeyDescriptor] = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactEmailAddressesKey as CNKeyDescriptor,
CNContactPhoneNumbersKey as CNKeyDescriptor,
CNContactImageDataAvailableKey as CNKeyDescriptor,
CNContactThumbnailImageDataKey as CNKeyDescriptor]
// 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)
results.append(contentsOf: containerResults)
} catch {
print("Error fetching results for container")
}
}
return results
}()
对于迭代和发现:
func searchForContactUsingPhoneNumber(phoneNumber: String) -> [CNContact] {
var result: [CNContact] = []
for contact in contacts {
if (!contact.phoneNumbers.isEmpty) {
let phoneNumberToCompareAgainst = phoneNumber.components(separatedBy: NSCharacterSet.decimalDigits.inverted).joined(separator: "")
for phoneNumber in contact.phoneNumbers {
if let phoneNumberStruct = phoneNumber.value as? CNPhoneNumber {
let phoneNumberString = phoneNumberStruct.stringValue
let phoneNumberToCompare = phoneNumberString.components(separatedBy: NSCharacterSet.decimalDigits.inverted).joined(separator: "")
if phoneNumberToCompare == phoneNumberToCompareAgainst {
result.append(contact)
}
}
}
}
}
return result
}