I am trying to write a simple method to ask a user for access to their address book and then print out the name of each person in the address book. I\'ve seen a number of tu
If anyone is also trying to get the email addresses of the contacts, I found that I needed to create two additional methods similar to the new one Wes showed.
Here's the updated version of the getContactNames() function:
func getContactNames()
{
var errorRef: Unmanaged<CFError>?
addressBook = extractABAddressBookRef(ABAddressBookCreateWithOptions(nil, &errorRef))
var contactList: NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
println("records in the array \(contactList.count)")
for record:ABRecordRef in contactList {
var contactPerson: ABRecordRef = record
var contactName: String = ABRecordCopyCompositeName(contactPerson).takeRetainedValue() as NSString
println ("contactName \(contactName)")
var emailArray:ABMultiValueRef = extractABEmailRef(ABRecordCopyValue(contactPerson, kABPersonEmailProperty))!
for (var j = 0; j < ABMultiValueGetCount(emailArray); ++j)
{
var emailAdd = ABMultiValueCopyValueAtIndex(emailArray, j)
var myString = extractABEmailAddress(emailAdd)
println("email: \(myString)")
}
}
}
And here are the two additional functions I created:
func extractABEmailRef (abEmailRef: Unmanaged<ABMultiValueRef>!) -> ABMultiValueRef? {
if let ab = abEmailRef {
return Unmanaged<NSObject>.fromOpaque(ab.toOpaque()).takeUnretainedValue()
}
return nil
}
func extractABEmailAddress (abEmailAddress: Unmanaged<AnyObject>!) -> String? {
if let ab = abEmailAddress {
return Unmanaged.fromOpaque(abEmailAddress.toOpaque()).takeUnretainedValue() as CFStringRef
}
return nil
}
Thanks again to Wes for his help on my initial question which helped me figure the above out.
There seems to be a bug either with the compiler or the framework where ABAddressBookRef
is declared a typealias of AnyObject
, but it needs to be NSObject
in order to unwrap it from the Unmanaged<ABAddressBookRef>!
returned by ABAddressBookCreateWithOptions
. A workaround is to convert it to and from an opaque C pointer. The following code works, but it should probably be doing a lot more error checking (and there is also probably a better way of working around this issue):
var addressBook: ABAddressBookRef?
func extractABAddressBookRef(abRef: Unmanaged<ABAddressBookRef>!) -> ABAddressBookRef? {
if let ab = abRef {
return Unmanaged<NSObject>.fromOpaque(ab.toOpaque()).takeUnretainedValue()
}
return nil
}
func test() {
if (ABAddressBookGetAuthorizationStatus() == ABAuthorizationStatus.NotDetermined) {
println("requesting access...")
var errorRef: Unmanaged<CFError>? = nil
addressBook = extractABAddressBookRef(ABAddressBookCreateWithOptions(nil, &errorRef))
ABAddressBookRequestAccessWithCompletion(addressBook, { success, error in
if success {
self.getContactNames()
}
else {
println("error")
}
})
}
else if (ABAddressBookGetAuthorizationStatus() == ABAuthorizationStatus.Denied || ABAddressBookGetAuthorizationStatus() == ABAuthorizationStatus.Restricted) {
println("access denied")
}
else if (ABAddressBookGetAuthorizationStatus() == ABAuthorizationStatus.Authorized) {
println("access granted")
self.getContactNames()
}
}
func getContactNames() {
var errorRef: Unmanaged<CFError>?
addressBook = extractABAddressBookRef(ABAddressBookCreateWithOptions(nil, &errorRef))
var contactList: NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
println("records in the array \(contactList.count)")
for record:ABRecordRef in contactList {
var contactPerson: ABRecordRef = record
var contactName: String = ABRecordCopyCompositeName(contactPerson).takeRetainedValue() as NSString
println ("contactName \(contactName)")
}
}
For those looking for the complete working solution, here is how to print out only the contact names, modifying the above code. Invoke getAddressBookNames()
to access the address book, e.g. in the viewDidLoad()
method.
func getAddressBookNames() {
let authorizationStatus = ABAddressBookGetAuthorizationStatus()
if (authorizationStatus == ABAuthorizationStatus.NotDetermined)
{
NSLog("requesting access...")
var emptyDictionary: CFDictionaryRef?
var addressBook = !ABAddressBookCreateWithOptions(emptyDictionary, nil)
ABAddressBookRequestAccessWithCompletion(addressBook,{success, error in
if success {
self.getContactNames();
}
else {
NSLog("unable to request access")
}
})
}
else if (authorizationStatus == ABAuthorizationStatus.Denied || authorizationStatus == ABAuthorizationStatus.Restricted) {
NSLog("access denied")
}
else if (authorizationStatus == ABAuthorizationStatus.Authorized) {
NSLog("access granted")
getContactNames()
}
}
func getContactNames()
{
var errorRef: Unmanaged<CFError>?
var addressBook: ABAddressBookRef? = extractABAddressBookRef(ABAddressBookCreateWithOptions(nil, &errorRef))
var contactList: NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
println("number of contacts: \(contactList.count)")
for record:ABRecordRef in contactList {
var contactName: String = ABRecordCopyCompositeName(record).takeRetainedValue() as NSString
NSLog("contactName: \(contactName)")
}
}
func extractABAddressBookRef(abRef: Unmanaged<ABAddressBookRef>!) -> ABAddressBookRef? {
if let ab = abRef {
return Unmanaged<NSObject>.fromOpaque(ab.toOpaque()).takeUnretainedValue()
}
return nil
}
And here is the complete code to access the contact names and emails - this is done using the helper methods defined in some of the other answers.
func getAddressBookNames() {
let authorizationStatus = ABAddressBookGetAuthorizationStatus()
if (authorizationStatus == ABAuthorizationStatus.NotDetermined)
{
NSLog("requesting access...")
var emptyDictionary: CFDictionaryRef?
var addressBook = !ABAddressBookCreateWithOptions(emptyDictionary, nil)
ABAddressBookRequestAccessWithCompletion(addressBook,{success, error in
if success {
self.processContactNames();
}
else {
NSLog("unable to request access")
}
})
}
else if (authorizationStatus == ABAuthorizationStatus.Denied || authorizationStatus == ABAuthorizationStatus.Restricted) {
NSLog("access denied")
}
else if (authorizationStatus == ABAuthorizationStatus.Authorized) {
NSLog("access granted")
processContactNames()
}
}
func processContactNames()
{
var errorRef: Unmanaged<CFError>?
var addressBook: ABAddressBookRef? = extractABAddressBookRef(ABAddressBookCreateWithOptions(nil, &errorRef))
var contactList: NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
println("records in the array \(contactList.count)")
for record:ABRecordRef in contactList {
processAddressbookRecord(record)
}
}
func processAddressbookRecord(addressBookRecord: ABRecordRef) {
var contactName: String = ABRecordCopyCompositeName(addressBookRecord).takeRetainedValue() as NSString
NSLog("contactName: \(contactName)")
processEmail(addressBookRecord)
}
func processEmail(addressBookRecord: ABRecordRef) {
let emailArray:ABMultiValueRef = extractABEmailRef(ABRecordCopyValue(addressBookRecord, kABPersonEmailProperty))!
for (var j = 0; j < ABMultiValueGetCount(emailArray); ++j) {
var emailAdd = ABMultiValueCopyValueAtIndex(emailArray, j)
var myString = extractABEmailAddress(emailAdd)
NSLog("email: \(myString!)")
}
}
func extractABAddressBookRef(abRef: Unmanaged<ABAddressBookRef>!) -> ABAddressBookRef? {
if let ab = abRef {
return Unmanaged<NSObject>.fromOpaque(ab.toOpaque()).takeUnretainedValue()
}
return nil
}
func extractABEmailRef (abEmailRef: Unmanaged<ABMultiValueRef>!) -> ABMultiValueRef? {
if let ab = abEmailRef {
return Unmanaged<NSObject>.fromOpaque(ab.toOpaque()).takeUnretainedValue()
}
return nil
}
func extractABEmailAddress (abEmailAddress: Unmanaged<AnyObject>!) -> String? {
if let ab = abEmailAddress {
return Unmanaged.fromOpaque(abEmailAddress.toOpaque()).takeUnretainedValue() as CFStringRef
}
return nil
}
This is an old question, but another answer may still be useful: I made an approach to solve the problems with address book in swift here: https://github.com/SocialbitGmbH/SwiftAddressBook
I should mention that there are many wrappers for ABAddressBook out there which can help you avoid issues like the one you asked about entirely. Thus I consider the link an "answer" to the problem (though it is not answering how to fix your code)
Not the best solution but until I find this work
let records = ABAddressBookCopyArrayOfAllPeople(self.addressBook).takeRetainedValue()
as NSArray as [ABRecord]
sleep(2)
println(records.count);
If you need email additionally to matt's answer:
func getContacts() {
if !self.determineStatus() {
println("not authorized")
}
let people = ABAddressBookCopyArrayOfAllPeople(adbk).takeRetainedValue() as NSArray as [ABRecord]
for person in people {
// Name
let name = ABRecordCopyCompositeName(person).takeRetainedValue()
// Email
let emails: ABMultiValueRef = ABRecordCopyValue(person, kABPersonEmailProperty).takeRetainedValue()
for (var i = 0; i < ABMultiValueGetCount(emails); i++) {
let email: String = ABMultiValueCopyValueAtIndex(emails, i).takeRetainedValue() as String
println("email=\(email)")
}
}
}