Accessing iOS Address Book with Swift: array count of zero

前端 未结 9 694
陌清茗
陌清茗 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:56

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

    Other answers provided here were useful, and guided this answer, but had errors and/or were not updated for Swift 3. The following class provides a number of simplifications and safety improvements.

    Usage is simply to call AddressBookService.getContactNames

    There are good reasons to still need to use the ABAddressBook framework, as CNContact does not provide some key data, including creation and modification dates for instance. The deprecated method warnings are somewhat distracting when working with the code, so this code suppresses the warnings that the ABAddressBook methods were deprecated from iOS 9 onwards, instead providing just a single warning to this effect wherever you call the class below.

    //
    //  AddressBookService.swift
    //
    
    import AddressBook
    
    @available(iOS, deprecated: 9.0)
    class AddressBookService: NSObject {
    
        class func getContactNames() {
            let authorizationStatus = ABAddressBookGetAuthorizationStatus()
    
            switch authorizationStatus {
            case .authorized:
                retrieveContactNames()
                break
    
            case .notDetermined:
                print("Requesting Address Book access...")
                let addressBook = AddressBookService.addressBook
                ABAddressBookRequestAccessWithCompletion(addressBook, {success, error in
                    if success {
                        print("Address book access granted")
                        retrieveContactNames()
                    }
                    else {
                        print("Unable to obtain Address Book access.")
                    }
                })
                break
    
            case .restricted, .denied:
                print("Address book access denied")
                break
            }
        }
    
        private class func retrieveContactNames() {
            let addressBook = ABAddressBookCreate().takeRetainedValue()
            let contactList = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue() as NSArray as [ABRecord]
    
            for (index, record) in contactList.enumerated() {
                if let contactName = ABRecordCopyCompositeName(record)?.takeRetainedValue() as String? {
                    print("Contact \(index): \(contactName))")
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 13:58

    To add to the info here, this is my solution pieced together from various places (is there a good Apple site that really describes this, the docs I've found basically provide almost nothing more than what the names of args/members are):

            let addrBook = ABAddressBookCreateWithOptions(nil,nil).takeRetainedValue()
            let contacts = ABAddressBookCopyArrayOfAllPeople(addrBook).takeRetainedValue() as NSArray as [ABRecordRef]
            for contact in contacts {
                let fname = ABRecordCopyValue(contact, kABPersonFirstNameProperty).takeRetainedValue() as! NSString
                let lname = ABRecordCopyValue(contact, kABPersonLastNameProperty).takeRetainedValue() as! NSString
                let name = String(fname) + " " + String(lname)
                var image:UIImage? = nil
                if ABPersonHasImageData(contact) {
                    image = UIImage(data: ABPersonCopyImageDataWithFormat(contact, kABPersonImageFormatThumbnail).takeRetainedValue() as NSData)
                }
                if let emailRefs: ABMultiValueRef = ABRecordCopyValue(contact, kABPersonEmailProperty).takeRetainedValue() {
    
                    let nEmailsForContact = ABMultiValueGetCount(emailRefs)
                    if  nEmailsForContact > 0 {
                        if let emailArray: NSArray = ABMultiValueCopyArrayOfAllValues(emailRefs).takeRetainedValue() as NSArray {
    
                            for emailW in emailArray {
                                let email = String(emailW)
                                if email.containsString("@") {
                                    let c: EmailContact = EmailContact(n: name, e: email, a: false, i: image)
                                    mEmailContacts.append(c)
                                }
                            }
                        }
                    }
                }
            }
    

    Oddly, you have to check to make sure there is an image if you want to access it; and you have to check that there is at least one email for a contact before trying to extract them (why doesn't it just return an empty list instead???).

    The 'EmailContact" class is something that I made to capture the results, its not shown but the code snippet does show how to extract the info for the current version of swift/ios.

    Also, I note that web site settings seem to come up in the EmailArray for contacts as well as actual emails. For now I just check for an "@" sign to determine if its really an email, but is there a better or 'official' way to do that?

    Finally, hopefully this is memory leak safe.

    Oh, of course this is done after getting permission, if you are not sure how to do that then this site is good: http://www.raywenderlich.com/63885/address-book-tutorial-in-ios

    0 讨论(0)
提交回复
热议问题