问题
I don't understand why my code doesn't compile with Swift.
I am trying to convert this Objective-C code:
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (addressBook != nil) {
NSLog(@"Succesful.");
NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
}
This is my current rendition in Swift:
var error:CFErrorRef
var addressBook = ABAddressBookCreateWithOptions(nil, nil);
if (addressBook != nil) {
println("Succesful.");
var allContacts:CFArrayRef = ABAddressBookCopyArrayOfAllPeople(addressBook);
}
but, Xcode reports:
'Unmanaged' is not convertible to 'CFArrayRef'
Do you guys have an idea ?
回答1:
Obviously, if targeting iOS version 9 or greater, you shouldn't use the AddressBook
framework at all, and instead use the Contacts
framework instead.
So,
Import
Contacts
:import Contacts
Make sure to supply a
NSContactsUsageDescription
in yourInfo.plist
.Then, you can then access contacts. E.g. in Swift 3:
let status = CNContactStore.authorizationStatus(for: .contacts) if status == .denied || status == .restricted { presentSettingsActionSheet() return } // open it let store = CNContactStore() store.requestAccess(for: .contacts) { granted, error in guard granted else { DispatchQueue.main.async { self.presentSettingsActionSheet() } return } // get the contacts var contacts = [CNContact]() let request = CNContactFetchRequest(keysToFetch: [CNContactIdentifierKey as NSString, CNContactFormatter.descriptorForRequiredKeys(for: .fullName)]) do { try store.enumerateContacts(with: request) { contact, stop in contacts.append(contact) } } catch { print(error) } // do something with the contacts array (e.g. print the names) let formatter = CNContactFormatter() formatter.style = .fullName for contact in contacts { print(formatter.string(from: contact) ?? "???") } }
Where
func presentSettingsActionSheet() { let alert = UIAlertController(title: "Permission to Contacts", message: "This app needs access to contacts in order to ...", preferredStyle: .actionSheet) alert.addAction(UIAlertAction(title: "Go to Settings", style: .default) { _ in let url = URL(string: UIApplicationOpenSettingsURLString)! UIApplication.shared.open(url) }) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) present(alert, animated: true) }
My original answer for AddressBook framework is below.
A couple of observations:
If you want to use
error
parameter ofABAddressBookCreateWithOptions
, define it to beUnmanaged<CFError>?
.If it fails, take a look at the error object (doing
takeRetainedValue
so you don't leak).Make sure to
takeRetainedValue
of the address book, too, so you don't leak.You probably shouldn't just grab the contacts, but you probably should request permission first.
Pulling that all together you get:
// make sure user hadn't previously denied access
let status = ABAddressBookGetAuthorizationStatus()
if status == .Denied || status == .Restricted {
// user previously denied, so tell them to fix that in settings
return
}
// open it
var error: Unmanaged<CFError>?
guard let addressBook: ABAddressBook? = ABAddressBookCreateWithOptions(nil, &error)?.takeRetainedValue() else {
print(error?.takeRetainedValue())
return
}
// request permission to use it
ABAddressBookRequestAccessWithCompletion(addressBook) { granted, error in
if !granted {
// warn the user that because they just denied permission, this functionality won't work
// also let them know that they have to fix this in settings
return
}
if let people = ABAddressBookCopyArrayOfAllPeople(addressBook)?.takeRetainedValue() as? NSArray {
// now do something with the array of people
}
}
来源:https://stackoverflow.com/questions/24852175/how-to-retrieve-address-book-contacts-with-swift