Accessing iOS Address Book with Swift: array count of zero Accessing iOS Address Book with Swift: array count of zero ios ios

Accessing iOS Address Book with Swift: array count of zero


This is now all much simpler. The chief thing to watch out for is that if you create an ABAddressBook without authorization, you get an evil address book - it isn't nil but it isn't good for anything either. Here's how I currently recommend that you set up authorization status and request authorization if necessary:

var adbk : ABAddressBook!func createAddressBook() -> Bool {    if self.adbk != nil {        return true    }    var err : Unmanaged<CFError>? = nil    let adbk : ABAddressBook? = ABAddressBookCreateWithOptions(nil, &err).takeRetainedValue()    if adbk == nil {        println(err)        self.adbk = nil        return false    }    self.adbk = adbk    return true}func determineStatus() -> Bool {    let status = ABAddressBookGetAuthorizationStatus()    switch status {    case .Authorized:        return self.createAddressBook()    case .NotDetermined:        var ok = false        ABAddressBookRequestAccessWithCompletion(nil) {            (granted:Bool, err:CFError!) in            dispatch_async(dispatch_get_main_queue()) {                if granted {                    ok = self.createAddressBook()                }            }        }        if ok == true {            return true        }        self.adbk = nil        return false    case .Restricted:        self.adbk = nil        return false    case .Denied:        self.adbk = nil        return false    }}

And here's how to cycle through all persons and print out their names:

func getContactNames() {    if !self.determineStatus() {        println("not authorized")        return    }    let people = ABAddressBookCopyArrayOfAllPeople(adbk).takeRetainedValue() as NSArray as [ABRecord]    for person in people {        println(ABRecordCopyCompositeName(person).takeRetainedValue())    }}


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}