7

I have an iOS app which needs access to the Contacts picker view controller in order to allow the user to select a contact property such as email address/ telephone numbers of imessage email addresses.

The problem I am having right now, is that I can't figure out how to parse the returned data. I have made use of the contactPicker didSelectContactProperty method, but I am unable to parse the data I need.

-(void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty {

   CNLabeledValue *test = contactProperty.contact.emailAddresses.firstObject;
   NSLog(@"%@", test);

   NSLog(@"%@", contactProperty.contact.phoneNumbers);
}

If you run the above code you get the following response:

2015-10-11 13:30:07.059 Actions[516:212765] <CNLabeledValue: 0x13656d090: identifier=21F2B1B2-8158-466B-9224-E2036CA07D28, label=_$!<Other>!$_, value=News_Europe@iEUNS.com> 2015-10-11 13:30:07.061 App_Name[516:212765] (
    "<CNLabeledValue: 0x13672a500: identifier=6697A0E9-3B91-4566-B26E-83B87979F816, label=_$!<Main>!$_, value=<CNPhoneNumber: 0x13672a660: countryCode=gb, digits=08000391010>>" )

Thats great, but how do I extract the data I need from it? Why are the NSLog statements returning the data in a weird format?

Thanks for your time, Dan.

Supertecnoboff
  • 6,406
  • 11
  • 57
  • 98

5 Answers5

15

The returned values are of the CNLabeledValue class. In order to get the value from them, for, say, the emails, do this

CNLabeledValue *emailValue = contactProperty.contact.emailAddresses.firstObject;
NSString *emailString = emailValue.value;

If the value you wanted a phone number, this is how you would retrieve that

CNLabeledValue *phoneNumberValue = contactProperty.contact.phoneNumbers.firstObject;
CNPhoneNumber *phoneNumber = phoneNumberValue.value;
NSString *phoneNumberString = phoneNumber.stringValue;

Because the returned value is a CNLabeledValue, you are also able to retrieve the phone number's or email's label

NSString *emailLabel = emailValue.label; //This may be 'Work', 'Home', etc.
NSString *phoneNumberLabel = phoneNumberValue.label;
benc
  • 1,381
  • 5
  • 31
  • 39
Chris Loonam
  • 5,735
  • 6
  • 41
  • 63
  • Ah right I see. Thanks so much. I have just upgraded from using the older AddressBook framework, so I was struggling with this. Thanks again :) – Supertecnoboff Oct 11 '15 at 13:08
  • 1
    Just one question, I am expecting the user to select either an email address of a telephone number. How can I check what they have selected? – Supertecnoboff Oct 11 '15 at 13:29
  • 2
    I think that that information would be stored in `contactProperty.value`, `contactProperty.label`, `contactProperty.key`, etc. I would test each of those and see what it outputs, as I've never used this before personally. – Chris Loonam Oct 11 '15 at 13:34
  • Awesome, ```contactProperty.key``` works perfectly. Thanks Chris :) – Supertecnoboff Oct 11 '15 at 13:39
  • I promise this will be my last question, but do you know if there is some sort of ```indexPath.row``` number I can use? (Instead of ```emailAddresses.firstObject```). So I could do ```emailAddresses[indexPath.row]```. – Supertecnoboff Oct 11 '15 at 13:52
  • If you mean that you need the index of the selected email address, I believe that `contactProperty.value` points to the email and that, as a result, you don't need to use the `emailAddresses` array. – Chris Loonam Oct 11 '15 at 13:54
  • Ah right I see. Brilliant, I promise I wont pester you anymore. Thanks :) – Supertecnoboff Oct 11 '15 at 13:56
  • Hey @ChrisLoonam, when I try to get the label of `CNLabeledValue`I get a text like this : "_$!!$_" – HamzaGhazouani Jun 16 '16 at 16:27
  • yes, i got this result, but i want to get only work email, not other email like home and etc. – Vinayak Nov 05 '16 at 11:56
  • 1
    To get properly localized labels instead of strings like "$!!$", use this code: [CNLabeledValue localizedStringForLabel:[phoneNumberValue label]]; – lifjoy Nov 16 '16 at 22:20
  • Would this still work if you've got multiple addresses and the user has selected anything other than the first address? – James Wolfe Sep 05 '18 at 09:40
2

Swift

This is the proper way how to do it:

func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
    if contactProperty.key == CNContactEmailAddressesKey,
       let emailAddress = contactProperty.value as? String
    {
        print(emailAddress)
    }
}

If you would select contactProperty.contact.emailAddresses.first, you would get the first email address of the contact which might not necessary be the email address the user has selected.

In case you are querying phone numbers, use the following body instead:

if let phoneNumber = contactProperty.value as? CNPhoneNumber {
    print(phoneNumber.stringValue)
}
Marián Černý
  • 15,096
  • 4
  • 70
  • 83
1

For swift 3.0 :

 public func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact)
{
      if let emailValue : CNLabeledValue = contact.emailAddresses.first
    {
        txtEmail.text = emailValue.value as String
    }
    if let phoneNumber : CNLabeledValue = contact.phoneNumbers.first
    {
        txtMobno.text = phoneNumber.value.stringValue
    }
     txtFname.text = contact.givenName + " " + contact.familyName

}
guru
  • 2,727
  • 3
  • 27
  • 39
1

Unfortunately Chris' answer tells you how to get the value from the CNLabeledValue object that is returned, but it doesn't tell you how to identify what CNLabeledValue was selected based on the contactProperty parameter the function features.

What you need to do is cycle through each of the contact's email addresses and check if it's identifier matches up with the selected contactProperty identifier. Use the following code inside the didSelectContactProperty function:

NSString *selectedEmail;

for (CNLabeledValue<NSString*>* email in contactProperty.contact.emailAddresses) {
    if ([email.identifier isEqualToString:contactProperty.identifier]) {
            selectedEmail = (NSString *)email.value;
    }
}

Note I have only tested this code with postal addresses, so it may require some further tweaking to work with email addresses.

James Wolfe
  • 340
  • 5
  • 18
0
Here is swift version of Chris answer :

 func fatchContacts(store : CNContactStore)  {
    do
    {
    let groups = try store.groups(matching: nil)
    let predicate =  CNContact.predicateForContactsInGroup(withIdentifier: groups[0].identifier)
    //let predicate = CNContact.predicateForContactsMatchingName("John")
        let keyToFatch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName ) ,CNContactEmailAddressesKey] as [Any]
    let contacts = try store.unifiedContacts(matching: predicate, keysToFetch: keyToFatch as! [CNKeyDescriptor])            //------------------------------------------------------
   //-------------Get Here-----------------------------------------
     print(contacts)
     print(contacts[0])
        let formatter = CNContactFormatter ()
        print(formatter.string(from: contacts[0]))
        print(contacts[0].givenName)
        print(contacts[0].emailAddresses)
        let emailValue : CNLabeledValue = contacts[0].emailAddresses.first!;
        let  email = emailValue.value
        print(email)




    }
    catch{

    }
}


Just pass the CNContactStore object        
guru
  • 2,727
  • 3
  • 27
  • 39