问题
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 ContactsMake sure to supply a
NSContactsUsageDescriptionin 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
errorparameter ofABAddressBookCreateWithOptions, define it to beUnmanaged<CFError>?.If it fails, take a look at the error object (doing
takeRetainedValueso you don't leak).Make sure to
takeRetainedValueof 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