4

As the title suggests, in my iOS app using swift I've got a CNContactProperty object and I want to extract the phone number from it as a string.

The CNContact property is returned from the standard CNContactPickerViewController via the delegate protocol function, after the user has selected a contact from it.

When a contact has multiple phone numbers, I want to be able to extract the one that the user tapped on in the contact view from the CNContactProperty.

I'm trying to do something like this:

 let myString = theCNContactProperty.value as! String

However, this crashes with an (lldb) error. I suspect that maybe the "value" property is not what I need?

I'm able to retrieve arbitrary numbers like so:

let myString = contactProperty.contact.phoneNumbers[0].value.stringValue

Which returns the first number a contact has. However, this doesn't serve my purpose as I want to be able to extract the specific number selected by the user when a contact has more than 1 number.

I've been working on this for hours and can't figure it out, I'd appreciate any help you can give me!

Edit: This is NOT a duplicate of the provided link. The linked question is about retrieving all numbers from a contact, NOT a specifically selected one. There is a HUGE difference in that.

Correct Answer:

As Mahdi Moqadasi wrote in the comments, the correct answer is to use (contactProperty.value as? CNPhoneNumber).stringValue.
Or see the following answers:
Extract email from CNContactProperty - iOS 9
iOS Objective C: Get user selected phone number from CNContactProperty as a string

Marián Černý
  • 15,096
  • 4
  • 70
  • 83
b1skit
  • 327
  • 4
  • 16
  • What's the type of `theCNContactProperty.value`? – dan Mar 06 '17 at 17:14
  • It's an "any" type value. I'm not even sure what its data represents, but "value" is suggestive that it might be what I'm looking for. However, if I try and cast it as a string like so, I get a crash: let test = contactProperty.value as! String – b1skit Mar 06 '17 at 17:24
  • The second example shown is the only way to do this so you are going to have to come up with a way to get the index of the selected number. – Timmy Mar 06 '17 at 17:27
  • @b1skit I know that it's declared type is `Any`, I'm asking what it's actual type is when you are accessing it. You can see it in the debugger by setting a breakpoint on that line or it should print the actual type in the crash log when your cast fails. – dan Mar 06 '17 at 17:29
  • I'm not sure, I think it's some kind of pointer?? Here's a copy/paste from the debugger: test Any? some payload_data_0 Builtin.RawPointer 0xffffffff payload_data_2 Builtin.RawPointer 0x12255c4 0x012255c4 CoreFoundation`__NSRetainCounters payload_data_2 Builtin.RawPointer 0x12255c4 0x012255c4 CoreFoundation`__NSRetainCounters instance_type Builtin.RawPointer 0x69cadc 0x0069cadc ..... (more) – b1skit Mar 06 '17 at 17:34
  • I'm not sure where you got that from but I'm going to assume the answer to my question is that it's a `CNPhoneNumber` and say you should change your `as! String` to `as! CNPhoneNumber` and then take the `stringValue`. – dan Mar 06 '17 at 17:36
  • 2
    this is the right way: `(contactProperty.value as? CNPhoneNumber).stringValue` if selected property is just `CNContactPhoneNumbersKey` – Mahdi Moqadasi Oct 07 '19 at 10:16

2 Answers2

0

You can use this - although, if the contact has more than one phone number, this will only get the first one...

var thePhoneLabel: String?
var thePhoneNumber: String?

func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {

    picker.dismissViewControllerAnimated(true, completion: {

        if contact.phoneNumbers.count > 0 {

            if let anEntry = contact.phoneNumbers.first {
                if let theNumber = anEntry.value as? CNPhoneNumber {

                    // Get the label for the phone number (Home, Work, Mobile, etc)
                    self.thePhoneLabel = CNLabeledValue.localizedStringForLabel(anEntry.label)

                    // Get the actual phone number (as a string)
                    self.thePhoneNumber = theNumber.stringValue

                }
            }

        } else {
            // contact has no phone numbers
            self.thePhoneLabel = "(No Phone)"
            self.thePhoneNumber = "(No Phone)"
        }

    })

}

EDIT:

If using:

contactPickerViewController.displayedPropertyKeys = [CNContactPhoneNumbersKey]

then:

var theContactName: String?
var thePhoneNumber: String?
var thePhoneLabel: String?

func contactPicker(picker: CNContactPickerViewController, didSelectContactProperty contactProperty: CNContactProperty) {

    theContactName = contactProperty.contact.givenName
    thePhoneNumber = contactProperty.value?.stringValue

    if let lbl = contactProperty.label {
        thePhoneLabel = CNLabeledValue.localizedStringForLabel(lbl)
    }

}

Note:

func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty)

function will only be called if you do NOT have a

func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact)

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Hi, thanks. I already have figured out how to retrieve the first number: let theNumber = contactProperty.contact.phoneNumbers[0].value.stringValue But unfortunately, I need to be able to get whichever specific number the user has selected, not only the 1st number in the contact. – b1skit Mar 06 '17 at 17:36
  • What function are you using to allow the user to select a specific phone number? – DonMag Mar 06 '17 at 17:43
  • I'm displaying the CNContactPickerViewController to the user, and catching its return value by implementing the CNContactPickerDelegate protocol function in my class. It gives me a CNContactProperty – b1skit Mar 06 '17 at 17:51
  • See my edits in my answer. – DonMag Mar 06 '17 at 18:06
0

So you said that you want to be able to get the phone number the user selected from the CNContactViewController.

The CNContactViewController has a delegate function that returns the key the user selected. This is the function:

optional func contactViewController(_ viewController: CNContactViewController, 
  shouldPerformDefaultActionFor property: CNContactProperty) -> Bool

In that function, you can get the selected phone number by doing this:

let myString = property.identifier

Also, if you return false in this function, the action wont take place which I think means it wont automatically call the number.

Timmy
  • 537
  • 3
  • 10
  • I can't get this to work. I tried adding the CNContactViewControllerDelegate interface to my class, and implementing the function you mentioned, but it doesn't get triggered by the CNContactPickerViewController. It seems that the CNContactPickerViewController only fires the functions in the CNContactPickerDelegate ? – b1skit Mar 06 '17 at 18:05
  • Would you mind updating your original post with this code so i can see how you implemented it? And also you are able to fight the duplicate post report. This is not a duplicate post! – Timmy Mar 07 '17 at 16:31
  • @Timmy - see my answer for implementing `didSelectContactProperty` – DonMag Mar 07 '17 at 18:35