Accessing iOS Address Book with Swift: array count of zero

前端 未结 9 693
陌清茗
陌清茗 2020-12-02 13:05

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

相关标签:
9条回答
  • 2020-12-02 13:39

    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.

    0 讨论(0)
  • 2020-12-02 13:40

    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)")
        }
    }
    
    0 讨论(0)
  • 2020-12-02 13:42

    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
    }
    
    0 讨论(0)
  • 2020-12-02 13:47

    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)

    0 讨论(0)
  • 2020-12-02 13:54

    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);
    
    0 讨论(0)
  • 2020-12-02 13:55

    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)")
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题