I want to access the user's Contacts and am planning to do so using the Contacts and ContactsUI framework that Apple supplies.
First, though, I need to ask permission to access the user's contacts and am having trouble doing so. In Swift 2, one could ask permission like so:
func requestForAccess(completionHandler: (accessGranted: Bool) -> Void) {
let authorizationStatus = CNContactStore.authorizationStatusForEntityType(CNEntityType.Contacts)
switch authorizationStatus {
case .Authorized:
completionHandler(accessGranted: true)
case .Denied, .NotDetermined:
self.contactStore.requestAccessForEntityType(CNEntityType.Contacts, completionHandler: { (access, accessError) -> Void in
if access {
completionHandler(accessGranted: access)
}
else {
if authorizationStatus == CNAuthorizationStatus.Denied {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let message = "\(accessError!.localizedDescription)\n\nPlease allow the app to access your contacts through the Settings."
self.showMessage(message)
})
}
}
})
default:
completionHandler(accessGranted: false)
}
}
I tried to convert it to Swift 3 like so, but am still coming up with errors. The error is "Instance member 'async' cannot be used on type 'DispatchQueue'; did you mean to use a value of this type instead?":
func requestForAccess(completionHandler: @escaping (_ accessGranted: Bool) -> Void) {
let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts)
switch authorizationStatus {
case .authorized:
completionHandler(true)
case .denied, .notDetermined:
self.contactStore.requestAccess(for: CNEntityType.contacts, completionHandler: { (access, accessError) -> Void in
if access {
completionHandler(access)
}
else {
if authorizationStatus == CNAuthorizationStatus.denied {
DispatchQueue.async(group: DispatchQueue.main, execute: { () -> Void in //error here
let message = "\(accessError!.localizedDescription)\n\nPlease allow the app to access your contacts through the Settings."
self.showMessage(message)
})
}
}
})
default:
completionHandler(false)
}
}
Can anyone help out to try to fix this? Any help would be immensely appreciated. Thanks a ton in advance.
Cheers, Theo
Swift 4 and 5. import ContactsUI func phoneNumberWithContryCode() -> [String] { let contacts = PhoneContacts. getContacts() // here calling the getContacts methods var arrPhoneNumbers = [String]() for contact in contacts { for ContctNumVar: CNLabeledValue in contact. phoneNumbers { if let fulMobNumVar = ContctNumVar.
In “Privacy,” tap “Contacts.” Next, you'll see a list of every installed app that has requested access to your contacts in the past. Beside each one, you'll see a switch that is either turned on or off. If the switch is set to “on,” the app can access your contacts.
Instead of
DispatchQueue.async(group: DispatchQueue.main, execute: { ... }
do
DispatchQueue.main.async { ... }
By the way, if permission had previously been denied, there's no point in requesting authorization again, because the OS will not present any "grant access" UI to the end user. It only does that if the user had not previously denied access.
If access to Contacts is truly essential for successful operation of the app, you can show them an alert that gives them the option of going to Settings directly from your app:
func requestAccess(completionHandler: @escaping (_ accessGranted: Bool) -> Void) {
switch CNContactStore.authorizationStatus(for: .contacts) {
case .authorized:
completionHandler(true)
case .denied:
showSettingsAlert(completionHandler)
case .restricted, .notDetermined:
store.requestAccess(for: .contacts) { granted, error in
if granted {
completionHandler(true)
} else {
DispatchQueue.main.async {
self.showSettingsAlert(completionHandler)
}
}
}
}
}
private func showSettingsAlert(_ completionHandler: @escaping (_ accessGranted: Bool) -> Void) {
let alert = UIAlertController(title: nil, message: "This app requires access to Contacts to proceed. Go to Settings to grant access.", preferredStyle: .alert)
if
let settings = URL(string: UIApplication.openSettingsURLString),
UIApplication.shared.canOpenURL(settings) {
alert.addAction(UIAlertAction(title: "Open Settings", style: .default) { action in
completionHandler(false)
UIApplication.shared.open(settings)
})
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in
completionHandler(false)
})
present(alert, animated: true)
}
Same thing, really but it treats no as a no, as opposed to maybe, not now or anything like that. Did you see any fuzzy logic in Apple privacy prompts? I did not.
fileprivate func requestForAccess(completionHandler: @escaping (_ accessGranted: Bool) -> Void) {
let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts)
switch authorizationStatus {
case .authorized:
completionHandler(true)
case .notDetermined:
self.contactStore.requestAccess(for: CNEntityType.contacts, completionHandler: { (access, accessError) -> Void in
if access {
completionHandler(access)
}
else {
completionHandler(false)
}
})
default:
completionHandler(false)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With