7

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

MegaManX
  • 8,766
  • 12
  • 51
  • 83

2 Answers2

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.

Jeremy Brown
  • 156
  • 1
  • 4
  • 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 change – MegaManX Oct 04 '17 at 09:27
  • 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 iCloud – Peter Johnson Jun 10 '21 at 15:38
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.

carlos21
  • 485
  • 4
  • 9
  • 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-9DA7B0A7D6F4 – MegaManX Oct 04 '17 at 09:39
  • @MegaManX 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. – carlos21 Oct 04 '17 at 15:37
  • 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. – MegaManX Oct 04 '17 at 15:45
  • 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. – Jeremy Brown Oct 18 '17 at 01:44