7

With ABAddressBook, when I wanted the user to be able to have the options of "Create New Contact" and "Add to Existing Contact" for a contact they hadn't seen before, I would create and present an ABUnknownPersonViewController.

I can find no way to replicate this functionality in the CNContacts framework. It seemed to me that CNContactViewController(forUnknownContact: contact) could work, but unfortunately this only lets the user "Send Message" or "Share Contact."

How can I allow a user to save the contact to their address book, either as a new contact or as part of an existing one, in CNContacts?

func presentContact() {

    let status = CNContactStore.authorizationStatusForEntityType(.Contacts)

    switch status {
    case .Authorized: ()
    case .NotDetermined: requestAccess()
    case .Denied, .Restricted: accessDenied()
    }

    print("authorized? \(status == .Authorized)") //prints "authorized? true"

    let unknown = CNContactViewController(forUnknownContact: contact!)

    unknown.delegate = self

    self.navigationController?.pushViewController(unknown, animated: false)

}

Even when I try to request access, the user still can't save the contact.

5813
  • 1,073
  • 3
  • 14
  • 28
  • Because you are not showing real code. Show your code! You are still leaving out all sorts of important pieces, and I can't tell whether that's because you don't know that you should do them or because you're just being lazy in pasting your code into Stack Overflow. What's `contact`? Show the line where you set `unknown`'s `contactStore`. Those things are all important. _SHOW. YOUR. REAL. CODE._ – matt Aug 28 '16 at 17:11
  • What important pieces am I still leaving out? `contact` is an arbitrary `CNContact` with any number of properties set. I'm not sure how that could have an effect on the action buttons not being shown. Everything is here except for `unknown`'s `contactStore` property being set, which turns out to be the problem. Thanks for your help. I'm sorry if I cut some corners in making a minimal reproducible example; I was trying to make it as simple and easy as possible to follow. – 5813 Aug 28 '16 at 17:25
  • I would accept this as an answer—or the answer you posted, if it still existed—because setting `contactStore` properly did resolve the issue. – 5813 Aug 28 '16 at 17:25

3 Answers3

21

You keep not showing your real code, so it's impossible to help you. So I've lost interest. I'll just show you my real code instead and leave you to study it and think about the difference between what I'm doing and what you're doing. Here's actual working code; go ye and do likewise:

let con = CNMutableContact()
con.givenName = "Johnny"
con.familyName = "Appleseed"
con.phoneNumbers.append(CNLabeledValue(
    label: "woods", value: CNPhoneNumber(stringValue: "555-123-4567")))
let unkvc = CNContactViewController(forUnknownContact: con)
unkvc.message = "He knows his trees"
unkvc.contactStore = CNContactStore()
unkvc.delegate = self
unkvc.allowsActions = false
self.navigationController?.pushViewController(unkvc, animated: true)

enter image description here

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Beware, however, of the bug I describe here: http://stackoverflow.com/q/32973254/341994 – matt Aug 28 '16 at 14:33
  • 1
    What's posted above _really_, _actually_ is my real code. Line for line, word for word, that's what I have in one of my `.swift` files. Not setting `contactStore` was the problem; you did help me. – 5813 Aug 28 '16 at 17:30
  • Worked for me !! – Pratik Pitale Aug 17 '18 at 06:49
  • 'back' button doesnt show up ! Every other thing works fine. Can you give me a work around on how to show the back button. My App is hosed on this screen only. – Zahurafzal Mirza Aug 29 '18 at 05:03
  • @ZahurafzalMirza Because you are not inside a navigation controller. – matt Aug 29 '18 at 05:05
  • Bro i am inside a navigation controller still the buttons didnt show up. However i ended up adding a left bar button item for my problem. – Zahurafzal Mirza Aug 31 '18 at 10:07
3
CNContactStore *store = [[CNContactStore alloc] init];
CNMutableContact *contact = [[CNMutableContact alloc] init];
CNPhoneNumber * number  = [[CNPhoneNumber alloc] initWithStringValue:@"1234567890"];
CNLabeledValue * labelValue = [[CNLabeledValue alloc]initWithLabel:CNLabelPhoneNumberMobile value:number];
NSMutableArray<CNLabeledValue *> *phoneNumbers = [NSMutableArray new];
[phoneNumbers addObject:labelValue];
contact.phoneNumbers = phoneNumbers;
CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:contact];

controller.contactStore = store;
controller.delegate = self;

[self presentViewController:[[UINavigationController alloc] initWithRootViewController:controller] animated:YES completion:nil];

This code will work for "Create New Contact", for "Add to Existing Contact" we will have to use CNContactPickerViewController

CNContactPickerViewController * picker = [[CNContactPickerViewController alloc] init];
                                    picker.delegate = self;
                                    [self presentViewController:picker animated:YES completion:nil];

and the in the delegate method

- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact
{
    CNContactStore *store = [[CNContactStore alloc] init];
    CNMutableContact *existingContact = [(CNMutableContact*)contact mutableCopy];
    CNPhoneNumber * number  = [[CNPhoneNumber alloc] initWithStringValue:@"1234567890"];
    CNLabeledValue * labelValue = [[CNLabeledValue alloc]initWithLabel:CNLabelPhoneNumberMobile value:number];
    NSMutableArray<CNLabeledValue *> *phoneNumbers = [NSMutableArray new];
    [phoneNumbers addObject:labelValue];
    [phoneNumbers addObjectsFromArray:existingContact.phoneNumbers];
    existingContact.phoneNumbers = phoneNumbers;
    CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:existingContact];

    controller.contactStore = store;
    controller.delegate = self;

    dispatch_async(dispatch_get_main_queue(), ^
    {
        [self presentViewController:[[UINavigationController alloc] initWithRootViewController:controller] animated:YES completion:nil];
    });

}

although it will show the Done button, instead of Update, but it will perform the functionality definitely as iPhone's default behaviour in Contacts App.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Raja Saad
  • 350
  • 4
  • 12
  • The question is tagged Swift, not Objective-C. Please post a Swift example. Thanks. – Eric Aya Dec 27 '18 at 10:14
  • @Moritz to just have an idea of what to do, Objective-C code is enough, and as a developer its not a big deal to understand the code whether its in Swift or Objective-C, plus there are tools which can convert the code, so plz.. – Raja Saad Dec 28 '18 at 06:40
  • 1
    I just got through this, and shared what I have implemented recently, when i'll get some free time, I'll do that too. – Raja Saad Dec 31 '18 at 06:34
2

What you're missing in your code is setting the contactStore property of your unknown variable to a handle of a CNContactStore.

[...]

unknown.contactStore = CNContactStore()

[...]