CNContacts framework possible bug with phone identifier?
Asked Answered
L

2

8

Recently I have switched from old ABAddressBook framework to new CNContacts. In my project I synchronize contacts from native with my own core data contacts. For this I use contact identifier and phone identifiers to synchronize phone numbers.

But I have observed interesting thing, when i try to edit the contact, I call this line of code

func getContact() -> CNContact? {
        let contactStore = CNContactStore()
        guard let contactRecord = try? contactStore.unifiedContact(withIdentifier: "8222B6F1-DE99-4099-82A4-47EAB9206A94:ABPerson", keysToFetch: [CNContactViewController.descriptorForRequiredKeys()]) else {
            return nil
        }
        return contactRecord
    }

@IBAction func showContact() {

        let contactViewController = CNContactViewController(forNewContact: self.getContact())
        contactViewController.delegate = self
        contactViewController.title = "New Contact"

        let navigationController = UINavigationController(rootViewController: contactViewController)
        navigationController.navigationBar.isTranslucent = false
        self.present(navigationController, animated: true, completion: nil)
    }

func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
        let newContact = self.getContact()
        viewController.dismiss(animated: false, completion: nil)
    }

It is really simple. But if in CNContactViewController I edit user photo, phone identifiers will change, even though I did no editing to phone numbers in this controller. The phone identifier can easily be printed like this:

po newContact?.phoneNumbers.first?.identifier

This really messes up my sync, since user will maybe just change photo, but identifiers for phone numbers will change, and I will have no way to know what happened. This identifier will not change if I edit some other data, like persons name, company etc... It will remain the same even if I edit the phone to some other value. But for some reason changing the photo messes it completely up.

Has anyone else observed this?

Here is link to sample project to test this https://drive.google.com/file/d/0B9ngBRq15jSuZTBYNVJCaVJ5WGc/view?usp=sharing

EDIT: I tested this on real phone

Lucillelucina answered 29/9, 2017 at 13:24 Comment(0)
B
3

I tried your sample project on an iPhone 8 simulator but can't seem to reproduce the issue. Here's the output right after I set an initial contact photo:

(lldb) po newContact?.phoneNumbers.first?.identifier
▿ Optional<String>
  - some : "E5D4EDC2-B6FC-4E86-9AF0-F6B78BAF41E5"

(lldb) po oldContact?.phoneNumbers.first?.identifier
▿ Optional<String>
  - some : "E5D4EDC2-B6FC-4E86-9AF0-F6B78BAF41E5"

And after I tap the button again and set a different photo as contact photo:

(lldb) po oldContact?.phoneNumbers.first?.identifier
▿ Optional<String>
  - some : "E5D4EDC2-B6FC-4E86-9AF0-F6B78BAF41E5"

(lldb) po newContact?.phoneNumbers.first?.identifier
▿ Optional<String>
  - some : "E5D4EDC2-B6FC-4E86-9AF0-F6B78BAF41E5"

Those all look the same to me. Only thing I did to your project was change the lookup in getContact() to an identifier that exists in my own contacts database.

Bantling answered 4/10, 2017 at 2:34 Comment(2)
Try several times doing this, but on real phone. Add photo, then save, delete photo then save, again add photo, and sure enough it will changeLucillelucina
One reason you may have got different results is if one of you was using iCloud, and the other was using Google contacts- I have observed identifiers changing on Google in ways they don't on iCloudHahnemann
G
2

I uploaded an example of how to create a new contact and how to edit it

https://drive.google.com/file/d/0B7OUqtQ1nSxjb3lFTFA5WnlGdFk/view?usp=sharing

For creating a new contact I'm using:

        let newContact = CNMutableContact()
        newContact.givenName = "Jhonny"
        newContact.nickname = "Jhonny"
        newContact.familyName = "My Family"
        newContact.phoneNumbers.append(CNLabeledValue(label: "Home", value: CNPhoneNumber(stringValue: "555-123-4567")))

        let contactViewController = CNContactViewController(forNewContact: newContact)
        contactViewController.delegate = self
        contactViewController.title = "New Contact"
        let navigationController = UINavigationController(rootViewController: contactViewController)
        self.present(navigationController, animated: true, completion: nil)

Pay attention to this line

let contactViewController = CNContactViewController(forNewContact: newContact)

For editing the contact I'm using:

        let contactViewController = CNContactViewController(for: contactRecord)
        contactViewController.delegate = self
        contactViewController.title = "Edit Contact"
        let navigationController = UINavigationController(rootViewController: contactViewController)
        self.present(navigationController, animated: true, completion: nil)

Again, pay attention to this line

let contactViewController = CNContactViewController(for: contactRecord)

I believe the problem is the way to call CNContactViewController. The constructor is different for creating and editing. In the demo, you'll see in the console log that the phone number identifier remains intact even if you edit the photo or anything else.

Guttery answered 4/10, 2017 at 2:43 Comment(4)
I tried your project and after several edits - add photo / delete photo, i managed to get different identifiers: **** contact givenName: Jhonny contact nickname: 0F386257-4071-4CC2-A2BE-18048F0F8F87:ABPerson Phone number identifier: D2F8B6D5-A2AC-4194-96F9-F915294E6445 *** contact givenName: Jhonny contact nickname: 0F386257-4071-4CC2-A2BE-18048F0F8F87:ABPerson Phone number identifier: DB940E27-3ADC-42A2-8EB3-9DA7B0A7D6F4Lucillelucina
@Lucillelucina I tried what you said ()deleting a photo) and you're right, the phone identifier changes. i tested this in iOS 10 and 11 and it's still happening. Can I ask What are you using those identifiers for? Maybe there's another way to achieve what you need.Guttery
i am using them to sync with my localdatabase, since phone number is not unique, ie person can have same numbers in his contact account.Lucillelucina
I would file a bug, which I think is unfortunately what you're hitting. You may need to do something like read the actual data in the fields and generate a hash of the data and use that as your identifier to detect changes, instead of relying on the system-provided identifier.Bantling

© 2022 - 2024 — McMap. All rights reserved.