CNContactFormatter.descriptorForRequiredKeys(for: .fullName) produces a blank CNContactViewController

I have a table view on which are listed some contacts. When I select one of the contacts, I want to show its details using the CNContactViewController.

Here's the

tableView(_:didDeselectRowAt:)
implementation for that situation:


func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  let selectedContact = contacts[indexPath.row]
  let keys = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey] as [Any]

   //  We’ll use to make sure that all the keys needed by the contacts view controller exist. This method accepts just one parameter, an array of keys or key descriptors (just like the keys array we used many times to fetch contacts). In the case of the CNContactViewController though we must set the CNContactViewController.descriptorForRequiredKeys() specific value as the only item of the parameter array, as that class method will automatically check for all the existing keys.
  if selectedContact.areKeysAvailable([CNContactViewController.descriptorForRequiredKeys()]) {
  // In case the keys are available, we’ll present the contacts view controller
  let contactViewController = CNContactViewController(for: selectedContact)

  contactViewController.contactStore = AppDelegate.getAppDelegate().contactStore
  contactViewController.displayedPropertyKeys = keys
  navigationController?.pushViewController(contactViewController, animated: true)
  } else {
  // In the opposite case, we’ll act similarly to what we previously did, and we’ll refetch the contact using the descriptorForRequiredKeys() for specifying the keys that should be fetched.
  AppDelegate.getAppDelegate().requestForAccess{ (accessGranted) -> Void in
  if accessGranted {
  do {
  let contactRefetched = try AppDelegate.getAppDelegate().contactStore.unifiedContact(withIdentifier: selectedContact.identifier, keysToFetch: [CNContactViewController.descriptorForRequiredKeys()])

  DispatchQueue.main.async {
  () -> Void in
  let contactViewController = CNContactViewController(for: contactRefetched)

  contactViewController.contactStore = AppDelegate.getAppDelegate().contactStore
  contactViewController.displayedPropertyKeys = keys
  self.navigationController?.pushViewController(contactViewController, animated: true)
  }
  } catch {
  print("Unabled to refetch the selected contact", separator: " ", terminator: "\n")
  }
  }
  }
  }
  }


The problem is that the resulting controller is blank and the following message is logged:


[CNUI ERROR] error calling service - Couldn’t communicate with a helper application.


I also noticed that if instead of keys, on lines 11 and 25 I use:


[CNContactGivenNameKey, CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey]


I got the desired resoult, I mean: a controller filled with the desired data. The question is: what's wrong with this code that results on a blank controller?

Replies

On line 3


  let keys = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey] as [Any]

You need as [Any] because of the first item which is a

CNKeyDescriptor
, not a String as others


Why do you need this one and not use for instance CNContactFamilyNameKey


  let keys = [CNContactFamilyNameKey, CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey] as [String]

or simply


let keys = [CNContactFamilyNameKey, CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey]

That's what you do when you change on line 11 or 26


If you need formatter CNContactFormatter.descriptorForRequiredKeys(for: .fullName), handle it separately.