How to display CNContact using CNContactViewController

I am trying to display CNContact on the new CNContactViewController. I am always getting "No Card Selected". I tried this with unsaved contact (no go). Also tried with saved+fetched from CNContactStore also no go. Tried with "me" contact but the same result. According to the debugger fetched contact is loaded with correct values and is not nil/empty. App is sandboxed and correctly asks for Contacts permission.

I know that under the hood Apple uses still ABPersonView, namely ABRemotePersonView. El Cap in addition created XPC service to display person. This is the reason I cannot stick with old AddressBook framework because e.g. ABPersonView returns each time new person object (due to CN -> AB conversion) and ABPersonView doesn't respect person's imageData.


Here is sample:



    #import <Contacts/Contacts.h>
    #import <ContactsUI/ContactsUI.h>
    #import "AppDelegate.h"
  
    @interface AppDelegate ()
  
    @property (weak) IBOutlet NSWindow *window;
    @property (strong)  CNContact *contact;
    @property (strong)  CNContactViewController *controller;
    @end
  
    @implementation AppDelegate
  
    - (instancetype)init {
      self = [super init];
      if (self) {
        _controller = [[CNContactViewController alloc] init];
      
      }
      return self;
    }
  
    - (void)awakeFromNib
    {
      CNContactStore *store = [[CNContactStore alloc] init];
      CNMutableContact *mutContact = [[CNMutableContact alloc] init];
      NSString *identifier = [mutContact identifier];
      mutContact.givenName = @"GivenName";
      mutContact.familyName = @"FamilyName";
      CNSaveRequest *saveRequest = [[CNSaveRequest alloc] init];
      [saveRequest addContact:mutContact toContainerWithIdentifier:[store defaultContainerIdentifier]];
      [store executeSaveRequest:saveRequest error:nil];
    
      id keysToFetch = @[[CNContactViewController descriptorForRequiredKeys]];
      _contact = [store unifiedContactWithIdentifier:identifier keysToFetch:keysToFetch error:nil];
    
      dispatch_async(dispatch_get_main_queue(), ^{
        self.controller.contact = self.contact;
        self.window.contentViewController = self.controller;
        self.window.contentView = self.controller.view;
      });
    }
    @end

This displays a new contact in edit-mode just fine:


import UIKit
import ContactsUI
class ViewController: UIViewController, CNContactViewControllerDelegate {
   let store = CNContactStore()
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        /
      
        let contact = CNMutableContact()
      
        contact.givenName = "John"
        contact.familyName = "Smith"
      
      
        let contactVC =
        CNContactViewController(forNewContact:  contact)
      
        self.presentViewController(contactVC, animated: true, completion: nil)
      
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        /
    }
}
...
self.store.requestAccessForEntityType(.Contacts, completionHandler: { (granted, error) in
              
                if (granted) {
                    do {
                        let completeContact = try self.store.unifiedContactWithIdentifier(contact.identifier, keysToFetch: [CNContactViewController.descriptorForRequiredKeys()])
                       
                        dispatch_async(dispatch_get_main_queue(), {
                            self.showContact(completeContact)
                        })
                       
                    } catch let e as NSError {
                        print(e.localizedDescription)
                        print(e.localizedRecoveryOptions)
               
                    }
                }
            })


Mine is in a view controller while yours appears in the app delegate. Not sure if that is the cause or not but you might consider moving code to a UIViewController. Although it's in Swift, this should translate fairly simply to Objc. I deal with the saveRequest and other methods after I confirm that the user has given permission to access their contacts.


If this solves your issue please click the link below otherwise update the thread.

How to display CNContact using CNContactViewController
 
 
Q