使用Swift访问iOS通讯簿:数组计数为零

use*_*648 45 abaddressbook ios swift

我正在尝试编写一种简单的方法来要求用户访问他们的地址簿,然后在地址簿中打印出每个人的姓名.我已经看过一些教程,解释了如何在Objective-C中做到这一点,但我很难将它们转换为swift.

这是我到目前为止所做的.下面的块在我的viewDidLoad()方法中运行,并检查用户是否具有对地址簿的授权访问权限,如果他们尚未授权访问,则第一个if语句将要求访问.此部分按预期工作.

var emptyDictionary: CFDictionaryRef?

var addressBook: ABAddressBookRef?

        if (ABAddressBookGetAuthorizationStatus() == ABAuthorizationStatus.NotDetermined)
        {
            println("requesting access...")
            addressBook = !ABAddressBookCreateWithOptions(emptyDictionary,nil)
            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")
            getContactNames()
        }
Run Code Online (Sandbox Code Playgroud)

一旦我知道用户已授予访问权限,我就运行下面的getContactNames()方法.经过多次来回,我终于能够通过添加takeRetainedValue()方法来编译它,以便将ABAddressBookCopyArrayOfAllPeople返回的数组从非托管数组转换为托管数组,这样我就可以将CFArrayRef转换为NSArray的.

我遇到的问题是contactList数组最终的计数为0,因此for循环被跳过.在我的模拟器中,地址簿有6或7条记录,所以我希望该数组具有该长度.有任何想法吗?

func getContactNames()
    {
        addressBook = !ABAddressBookCreateWithOptions(emptyDictionary,nil)
        var contactList: NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
        println("records in the array \(contactList.count)") // returns 0

        for record:ABRecordRef in contactList {
            var contactPerson: ABRecordRef = record
            var contactName: String = ABRecordCopyCompositeName(contactPerson).takeRetainedValue()
            println ("contactName \(contactName)")
        }
    }
Run Code Online (Sandbox Code Playgroud)

还有一点 - 如果我使用ABAddressBookGetPersonCount方法,它返回-1.

 var count: CFIndex = ABAddressBookGetPersonCount(addressBook);
        println("records in the array \(count)") // returns -1
Run Code Online (Sandbox Code Playgroud)

基于此链接ABAddressBookGetPersonCount在iOS中返回-1,似乎此函数返回-1可能与未授予权限相关,但我肯定已经在上面的代码中请求了权限(并在我运行应用程序时授予它模拟器)

mat*_*att 27

现在这简单得多了.需要注意的首要事项是,如果你在未经授权的情况下创建一个ABAddressBook,你就会得到一本邪恶的地址簿 - 它不是零,但它对任何东西都不好.以下是我目前建议您设置授权状态并在必要时请求授权的方法:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是如何循环所有人并打印出他们的名字:

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())
    }
}
Run Code Online (Sandbox Code Playgroud)


Wes*_*gne 15

似乎有一个错误的编译器或框架在哪里ABAddressBookRef被声明为typealias AnyObject,但它需要是NSObject为了从Unmanaged<ABAddressBookRef>!返回的包装中解包它ABAddressBookCreateWithOptions.解决方法是将其转换为不透明的C指针.下面的代码可以工作,但它可能应该进行更多的错误检查(并且可能有更好的解决此问题的方法):

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)")
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 很棒的解决方案,但幸好现在没必要,因为`ABAddressBookCreateWithOptions`现在返回一个`Unmanaged <ABAddressBook>!`,你可以直接`takeRetainedValue()`. (2认同)

Ger*_*osz 8

对于那些寻找完整工作解决方案的人来说,这里是如何打印联系人名称,修改上述代码.调用getAddressBookNames()以访问地址簿,例如在viewDidLoad()方法中.

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
}
Run Code Online (Sandbox Code Playgroud)

以下是访问联系人姓名和电子邮件的完整代码- 这是使用其他一些答案中定义的帮助程序方法完成的.

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
}
Run Code Online (Sandbox Code Playgroud)