Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Contact Framework equivalent to ABAddressBook.ABAddressBookRegisterExternalChangeCallback

I am migrating an application from the deprecated Address Book Framework to the new Contacts Framework. The application utilizes ABAddressBookRegisterExternalChangeCallback to be notified when another application changes a contact.

I am unable to find equivalent functionality in the Contacts Framework. Apple documentation says to use the default notification center with the CNContactStoreDidChangeNotification notification:

The notification posted when changes occur in another CNContactStore.

Taking Apple's advice, my code looks like this:

NSNotificationCenter.defaultCenter().addObserver(
    self,
    selector: "contactsChanged:",
    name: CNContactStoreDidChangeNotification,
    object: nil)

However, I have found two problems with this approach:

  1. I am notified for all changes, including those made by my own application.
  2. Notifications are spurious - I receive many notifications for a single change.

If I log the debug description of the notification when the change was made within my app, I get something like this:

NSConcreteNotification 0x7d3370e0 {name = CNContactStoreDidChangeNotification; userInfo = {
    CNNotificationOriginationExternally = 1;
    CNNotificationSourcesKey =     (
    );
}}

And if the changes are made externally:

NSConcreteNotification 0x7bf7a690 {name = CNContactStoreDidChangeNotification; userInfo = {
    CNNotificationOriginationExternally = 1;
    CNNotificationSourcesKey =     (
    );
}}

As you can see, nothing obvious with which to distinguish them.

Can anyone tell me how to get the same behavior from the Contacts Framework as one can get from ABAddressBookRegisterExternalChangeCallback?

like image 895
me-- Avatar asked Mar 22 '16 09:03

me--


1 Answers

First, I'd recommend filing a bug with Apple about the lack of a way to identify internal vs external changes in the API.

As a possible workaround, you could see if unregistering your observer before making a change and re-registering immediately afterward ensures that you miss all of your change notifications and still get all the external ones:

class ContactsThingy {

    var observer: NSObjectProtocol?
    let contacts = CNContactStore()

    func contactStoreDidChange(notification: NSNotification) {
        NSLog("%@", notification)
    }

    func registerObserver() {
        let center = NSNotificationCenter.defaultCenter()
        observer = center.addObserverForName(CNContactStoreDidChangeNotification, object: nil, queue: NSOperationQueue.currentQueue(), usingBlock: contactStoreDidChange)
    }

    func unregisterObserver() {
        guard let myObserver = observer else { return }
        let center = NSNotificationCenter.defaultCenter()
        center.removeObserver(myObserver)
    }

    func changeContacts(request: CNSaveRequest) {
        unregisterObserver() // stop watching for changes
        defer { registerObserver() } // start watching again after this change even if error
        try! contacts.executeSaveRequest(request)
    }
}
like image 186
rickster Avatar answered Oct 17 '22 23:10

rickster