Here is my code for getting the name of a contact, how would I go about getting their phone number?
func createAddressBook() -> Bool {
if self.addressBook != nil {
return true
}
var err : Unmanaged<CFError>? = nil
let addressBook : ABAddressBook? = ABAddressBookCreateWithOptions(nil, &err).takeRetainedValue()
if addressBook == nil {
println(err)
self.addressBook = nil
return false
}
self.addressBook = addressBook
getContactNames()
return true
}
func getContactNames() {
if !self.determineStatus() {
println("not authorized")
return
}
let people = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue() as NSArray as [ABRecord]
for person in people {
var contactName = ABRecordCopyCompositeName(person).takeRetainedValue() as String
self.contacts.append(contact(name: contactName))
}
}
Any help would be greatly appreciated.
As of iOS 9, we would use Contacts framework, in which phoneNumbers
is a CNLabeledValue<CNPhoneNumber>
:
let status = CNContactStore.authorizationStatus(for: .contacts)
if status == .denied || status == .restricted {
presentSettingsAlert()
return
}
// open it
let store = CNContactStore()
store.requestAccess(for: .contacts) { granted, error in
guard granted else {
self.presentSettingsAlert()
return
}
// get the contacts
let request = CNContactFetchRequest(keysToFetch: [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactPhoneNumbersKey as CNKeyDescriptor])
do {
try store.enumerateContacts(with: request) { contact, stop in
let name = CNContactFormatter.string(from: contact, style: .fullName)
print(name)
for phone in contact.phoneNumbers {
var label = phone.label
if label != nil {
label = CNLabeledValue<CNPhoneNumber>.localizedString(forLabel: label!)
}
print(" ", label, phone.value.stringValue)
}
}
} catch {
print(error)
}
}
Where
private func presentSettingsAlert() {
let settingsURL = URL(string: UIApplicationOpenSettingsURLString)!
DispatchQueue.main.async {
let alert = UIAlertController(title: "Permission to Contacts", message: "This app needs access to contacts in order to ...", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Go to Settings", style: .default) { _ in
UIApplication.shared.openURL(settingsURL)
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
self.present(alert, animated: true)
}
}
Prior to iOS 9, you would use the AddressBook framework, in which the phone numbers is a ABMultiValueRef
, so get that reference and then iterate through the phone numbers:
// make sure user hadn't previously denied access
let status = ABAddressBookGetAuthorizationStatus()
if status == .denied || status == .restricted {
presentSettingsAlert()
return
}
// open it
var error: Unmanaged<CFError>?
guard let addressBook: ABAddressBook? = ABAddressBookCreateWithOptions(nil, &error)?.takeRetainedValue() else {
print(String(describing: error?.takeRetainedValue()))
return
}
// request permission to use it
ABAddressBookRequestAccessWithCompletion(addressBook) { granted, error in
if !granted {
self.presentSettingsAlert()
return
}
guard let people = ABAddressBookCopyArrayOfAllPeople(addressBook)?.takeRetainedValue() as [ABRecord]? else {
print("unable to get contacts")
return
}
for person in people {
let name = ABRecordCopyCompositeName(person)?.takeRetainedValue() as String?
print(name)
if let phoneNumbers: ABMultiValue = ABRecordCopyValue(person, kABPersonPhoneProperty)?.takeRetainedValue() {
for index in 0 ..< ABMultiValueGetCount(phoneNumbers) {
let number = ABMultiValueCopyValueAtIndex(phoneNumbers, index)?.takeRetainedValue() as? String
let label = ABMultiValueCopyLabelAtIndex(phoneNumbers, index)?.takeRetainedValue()
print(" ", self.localizedLabel(label), number)
}
}
}
}
MacOS has an existing routine to localize that label, but I don't know of any such public function in AddressBook framework for iOS, so you may want to convert it yourself (or populate localization table for NSLocalizedString
):
// frankly, you probably should just use `NSLocalizedString()` and fill the table with these values
private func localizedLabel(_ label: CFString?) -> String? {
guard let label = label else {
return nil
}
if CFStringCompare(label, kABHomeLabel, []) == .compareEqualTo { // use `[]` for options in Swift 2.0
return "Home"
} else if CFStringCompare(label, kABWorkLabel, []) == .compareEqualTo {
return "Work"
} else if CFStringCompare(label, kABOtherLabel, []) == .compareEqualTo {
return "Other"
} else if CFStringCompare(label, kABPersonPhoneMobileLabel, []) == .compareEqualTo {
return "Mobile"
} else if CFStringCompare(label, kABPersonPhoneIPhoneLabel, []) == .compareEqualTo {
return "iPhone"
} else if CFStringCompare(label, kABPersonPhoneMainLabel, []) == .compareEqualTo {
return "Main"
} else if CFStringCompare(label, kABPersonPhoneHomeFAXLabel, []) == .compareEqualTo {
return "Home fax"
} else if CFStringCompare(label, kABPersonPhoneWorkFAXLabel, []) == .compareEqualTo {
return "Work fax"
} else if CFStringCompare(label, kABPersonPhoneOtherFAXLabel, []) == .compareEqualTo {
return "Other fax"
} else if CFStringCompare(label, kABPersonPhonePagerLabel, []) == .compareEqualTo {
return "Pager"
} else {
return label as String
}
}
For Swift 2, see previous revision of this answer.