3

I'm trying to save a contact to the contacts app by using this code directly without requesting any permission:

import Foundation
import UIKit
import Contacts

@available(iOS 9.0, *)
class OnCallEmpContact: UITableViewController {

    var store: CNContactStore!

    @IBOutlet weak var nameLabel: UILabel!

    @IBOutlet weak var phoneLabel: UILabel!

    @IBOutlet weak var emailLabel: UILabel!

    @IBOutlet weak var workPhoneLabel: UILabel!



    var emp = employee()
    var rank = ""

    override func viewDidLoad() {
        super.viewDidLoad()
        self.nameLabel.text=self.emp.getFistName()+" "+emp.getLastName()
        self.phoneLabel.text="0"+self.emp.getMobile()
        self.emailLabel.text=self.emp.getEmail()
        self.workPhoneLabel.text=self.emp.getPhone()

        store = CNContactStore()
        checkContactsAccess()


    }

    private func checkContactsAccess() {
        switch CNContactStore.authorizationStatusForEntityType(.Contacts) {
            // Update our UI if the user has granted access to their Contacts
        case .Authorized:
            self.accessGrantedForContacts()

            // Prompt the user for access to Contacts if there is no definitive answer
        case .NotDetermined :
            self.requestContactsAccess()

            // Display a message if the user has denied or restricted access to Contacts
        case .Denied,
        .Restricted:
            let alert = UIAlertController(title: "Privacy Warning!",
                message: "Permission was not granted for Contacts.",
                preferredStyle: .Alert)
            alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
            self.presentViewController(alert, animated: true, completion: nil)
        }
    }

    private func requestContactsAccess() {

        store.requestAccessForEntityType(.Contacts) {granted, error in
            if granted {
                dispatch_async(dispatch_get_main_queue()) {
                    self.accessGrantedForContacts()
                    return
                }
            }
        }
    }

    // This method is called when the user has granted access to their address book data.
    private func accessGrantedForContacts() {
        //Update UI for grated state.
        //...
    }


    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if section == 0{
        if rank == "0"{
        return "Primary Employee"
        }
        else if rank == "1"{
        return "Backup Employee"
        }
        }
        return""
    }

    override  func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

        tableView.deselectRowAtIndexPath(indexPath, animated: true) // to stop highliting the selected cell

        if indexPath.section==1 && indexPath.row==0{
                self.saveContact()
        }


    }



    func saveContact(){
        do{
            let contact = CNMutableContact()
        contact.givenName = self.emp.getFistName()
        contact.familyName = self.emp.getLastName()
        contact.phoneNumbers = [CNLabeledValue(
            label:CNLabelPhoneNumberiPhone,
            value:CNPhoneNumber(stringValue:emp.getMobile())),
                  CNLabeledValue(
                label:CNLabelPhoneNumberiPhone,
                value:CNPhoneNumber(stringValue:emp.getPhone()))]

        let workEmail = CNLabeledValue(label:CNLabelWork, value:emp.getEmail())
        contact.emailAddresses = [workEmail]


        let saveRequest = CNSaveRequest()
        saveRequest.addContact(contact, toContainerWithIdentifier:nil)
        try store.executeSaveRequest(saveRequest)
            print("saved")

        }

        catch{
            print("error")
        }
    }



}

I'm getting error at the last line try store.executeSaveRequest(saveRequest) of method saveContact

Error :

fatal error: 'try!' expression unexpectedly raised an error: Error Domain=CNErrorDomain Code=100 "Access Denied" UserInfo={NSLocalizedDescription=Access Denied, NSLocalizedFailureReason=This application has not been granted permission to access Contacts.

I found the above code at apple website but also I red this on Privacy section in the middle :

Any call to CNContactStore will block the application while the user is being asked to grant or deny access

But nothing show up asking for access to the contacts app .. should I write more code to do this ? How ?

Zizoo
  • 1,694
  • 5
  • 20
  • 42

3 Answers3

1

It's wrong! Use do-catch statement and you'll be OK.

do {
  try store.executeSaveRequest(saveRequest)
} catch {}

Hope it helps.

Mannopson
  • 2,634
  • 1
  • 16
  • 32
0

This is the newest syntax from swift. Hope it helps !

           let store = CNContactStore()

            if CNContactStore.authorizationStatus(for: .contacts) == .notDetermined {
                store.requestAccess(for: .contacts) {granted, error in
                    if granted {
                        print("PERMISSION GRANTED")
                    } else {
                        print("PERMISSION NOT GRANTED")
                    }
                }
            } else if CNContactStore.authorizationStatus(for: .contacts) == .authorized {
                // If the user user has earlier provided the access, then add the contact
                print("AUTHORIZED")

                findContactsOnBackgroundThread { (contact) in
                    print("contacts : ",contact as Any)
                }
            } else {
                // If the user user has NOT earlier provided the access, create an alert to tell the user to go to Settings app and allow access
                print("NOT AUTHORIZED")
            }
-1

Maybe you have read Contacts Framework Reference. You should think such overall description can be easily inaccurate in details. Check the latest reference.

CNContactStore has two methods related to authorization:

Privacy Access

+ authorizationStatusForEntityType:

- requestAccessForEntityType:completionHandler:

You should write something like this in your app:

var store: CNContactStore!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    store = CNContactStore()
    checkContactsAccess()
}

private func checkContactsAccess() {
    switch CNContactStore.authorizationStatusForEntityType(.Contacts) {
    // Update our UI if the user has granted access to their Contacts
    case .Authorized:
        self.accessGrantedForContacts()
        
    // Prompt the user for access to Contacts if there is no definitive answer
    case .NotDetermined :
        self.requestContactsAccess()
        
    // Display a message if the user has denied or restricted access to Contacts
    case .Denied,
         .Restricted:
        let alert = UIAlertController(title: "Privacy Warning!",
                                      message: "Permission was not granted for Contacts.",
                                      preferredStyle: .Alert)
        alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
        self.presentViewController(alert, animated: true, completion: nil)
    }
}

private func requestContactsAccess() {
    
    store.requestAccessForEntityType(.Contacts) {granted, error in
        if granted {
            dispatch_async(dispatch_get_main_queue()) {
                self.accessGrantedForContacts()
                return
            }
        }
    }
}

// This method is called when the user has granted access to their address book data.
private func accessGrantedForContacts() {
    //Update UI for grated state.
    //...
}
Community
  • 1
  • 1
OOPer
  • 47,149
  • 6
  • 107
  • 142
  • Thank you, but when the app reach requestContactsAccess() why it not asking for the request to the user ? + where to call my function saveContact() ? – Zizoo Sep 04 '16 at 10:24
  • @Zizoo, just my guess, when you first used the app, you might have denied or granted and the sim or device stores the state. Check Setting > Privacy > Contacts, or try with brand-new sim or device. For the latter, add a "save" button to your app and call the method in the @IBAction method. One more, better avoid using `try!` in actual apps. Use simple `try` in `do {} catch {}`. – OOPer Sep 04 '16 at 10:39
  • ok what this method used for 'accessGrantedForContacts' because I'm taking the information of contact from an object of employee I have and when the button pressed I call the save function, I'm sorry i'm new to iOS – Zizoo Sep 04 '16 at 11:14
  • I used the your code but still nothing show up asking for access also I changed the sim and still not working and getting the same error of access denied – Zizoo Sep 04 '16 at 11:21
  • @Zizoo, if you use my `checkContactsAccess`, you should see the "Privacy Warning!" alert in denied state. So, it's really an odd behavior. Can you show me the versions of your Xcode and the simulators you used? – OOPer Sep 04 '16 at 11:36
  • Yes I see the Privacy Warning but no asking for request to the contacts – Zizoo Sep 04 '16 at 11:38
  • Xcode Version 7.2.1 , I used sim : Version 9.2 (SimulatorApp-643 CoreSimulator-201.3) – Zizoo Sep 04 '16 at 11:40
  • If you see that "Privacy Warning!", then your sim is keeping the denied state for your app. Try "Reset Content and Settings..." in the Simulator menu to clear the state (or choose another brand-new sim). And, when in denied state, you need to disable all UIs (like "save" buttons) to prevent accessing `CNContactStore`. Any access to `CNContactStore` in denied state, may raise the same error as you have seen. – OOPer Sep 04 '16 at 11:48
  • I changed the sim .. The code go from start in `viewDidLoad` `checkContactsAccess()` then to case `NotDetermined` then will go to `requestContactsAccess()` and nothing show up asking for request , then I go back by navigation button to previews view and go again to view2 and I get the warning , now it keep going to case `Denied,.Restricted` – Zizoo Sep 04 '16 at 12:27
  • @Zizoo, I do not understand your "navigation button to previews view" or "view2" things, but my code is taken from an actually working sample code. So, something not shown would be affecting, but I cannot say any more without seeing them and I have no clue what I should request you to show me as for now. – OOPer Sep 04 '16 at 12:36
  • Sorry, but I think the "chat" system is the worst thing in SO, simply I don't like it. If you have updated your question with some more info which I need, the I'll watch on it. – OOPer Sep 04 '16 at 12:47
  • @Zizoo, I tried your code (exactly the same, no single character modified) as the second view controller (filled some other parts) and I get `"MyAppName" Would Like to Access Your Contacts` dialog with `Don't Allow` and `OK` on its first use. The problem may be hiding in other parts of your code. – OOPer Sep 04 '16 at 13:20
  • Thank you for your help .. but you saw my code nothing wrong with it , may be the problem with the sim .. – Zizoo Sep 04 '16 at 16:23
  • It is too early to check for contacts access in `viewDidLoad`. Try in `viewDidAppear`. The alert can then be presented without warning "Presenting view controllers on detached view controllers is discouraged" (iOS10.3+) – ghr Apr 26 '20 at 06:54