4

I want to import all phone contacts into app first time only and save on server.

Second time wants to imports only new, modified and deleted contacts into an app and sync with contacts saved on the server which should be done according to created/modified date of contacts.

From iOS 9, Apple won't allow to get create/modified date using Contacts Framework

How can I achieve with better approach?

johnnyRose
  • 7,310
  • 17
  • 40
  • 61
Harshal Wani
  • 2,249
  • 2
  • 26
  • 41

3 Answers3

1

You should add observer for change contact list notification like this:

    NotificationCenter.default.addObserver(self,selector: #selector(self.addressBookDidChange(_:)), name: NSNotification.Name.CNContactStoreDidChange,object: nil)

And then

@objc func addressBookDidChange(_ notification: Notification){
    print(notification.object as Any, notification.userInfo as Any)
    //remove observer so far to prevent double method calling when making operations with contacts
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.CNContactStoreDidChange, object: nil)

    //processing


    //at some point return observer
    NotificationCenter.default.addObserver(self,selector: #selector(ContactsListVC.addressBookDidChange(_:)), name: NSNotification.Name.CNContactStoreDidChange,object: nil)



}
nerowolfe
  • 4,787
  • 3
  • 20
  • 19
  • Hi Nerowife, thanks for the answer. But in our app, A user can update contact from both phone app as well as from server(web app). I already tried this solution but I cant able to differentiate from where the latest contact is modified. For this, I need to send contact timestamp(modified) to API from the phone. So we can update the contact in database accordignly. – Harshal Wani Jan 20 '18 at 06:22
1

You can save contacts identifier and import date in local storage like SQLite. And when you receive contacts update notification, you can send saved date of respective updated contact and also update date in SQLite with received notification timestamp(date).

0

There's an undocumented API in CNContactStore - enumeratorForChangeHistoryFetchRequest:error:

I tried to test this method using the following code:

-(void)testChangeHistoryRequest {
    CNContactStore *store = [[CNContactStore alloc] init];
    [self requestContactsPermissions:store completion:^(BOOL granted) {
        if (!granted)
            return;
        NSData *storeToken = [store currentHistoryToken];
        NSLog(@"testChangeHistoryRequest: store token st start - %@", storeToken);
        NSError *error;
        CNChangeHistoryFetchRequest *req = [[CNChangeHistoryFetchRequest alloc] init];
        [req setAdditionalContactKeyDescriptors:@[CNContactGivenNameKey, CNContactFamilyNameKey]];
        [req setMutableObjects:YES];
        [req setShouldUnifyResults:YES];
        [req setStartingToken:storeToken];
        CNFetchResult<NSEnumerator<CNChangeHistoryEvent*>*>* res = [store enumeratorForChangeHistoryFetchRequest:req error:&error];
        if (res && res.value) {
            NSData *token = [res currentHistoryToken];
            NSLog(@"token - %@", token);
            for (CNChangeHistoryEvent *e in res.value)
                NSLog(@"%@ - %@", [e class], e);
            NSLog(@"token at end - %@", token);
        }
    }];
}

What I got is that store.currentHistoryToken never changes - it starts and ends with nil value. Also during the iteration, res.currentHistoryToken is always nil.

I also tried to initialize [req setStartingToken:storeToken]; with arbitrary data, but this changed nothing (and didn't even fail).

My guess is that this enumeration is not fully implemented.

grebulon
  • 7,697
  • 5
  • 42
  • 66
  • This isn't undocumented ... it's part of changes made to the Contacts framework in iOS13. However I'm also running into the same issue, where the `currentHistoryToken` is nil for the `CNFetchResult` object, making it useless. – Z S Apr 19 '20 at 01:06
  • enumeratorForChangeHistoryFetchRequest is not available for Swift, unfortunately. – daniel Dec 10 '21 at 04:08
  • 1
    You can use this Swift adapter: https://github.com/yonat/ContactsChangeNotifier . It can manage the history token logic for you. – Yonat Jul 18 '22 at 10:29