iOS 13 added an API for that, but sadly it's only accessible from Objective-C code: enumeratorForChangeHistoryFetchRequest:error:
To use it from Swift, you will need to create a wrapper:
CNContactStore+ChangeHistory.h
#import <Contacts/Contacts.h>
NS_ASSUME_NONNULL_BEGIN
@interface CNContactStore (ChangeHistory)
- (CNFetchResult<NSEnumerator<CNChangeHistoryEvent *> *> *)swiftEnumeratorForChangeHistoryFetchRequest:(CNChangeHistoryFetchRequest *)request
error:(NSError * _Nullable *)error;
@end
CNContactStore+ChangeHistory.m
#import "CNContactStore+ChangeHistory.h"
@implementation CNContactStore (ChangeHistory)
- (CNFetchResult<NSEnumerator<CNChangeHistoryEvent *> *> *)swiftEnumeratorForChangeHistoryFetchRequest:(CNChangeHistoryFetchRequest *)request
error:(NSError * _Nullable *)error
{
return [self enumeratorForChangeHistoryFetchRequest:request error:error];
}
@end
To ensure you only get new changes (and not the complete change history) you will need to save CNContactStore().currentHistoryToken
when you get contacts. Then later pass this token in the CNChangeHistoryFetchRequest
:
var myToken = myContactStore.currentHistoryToken
// ... then later, after you get CNContactStoreDidChange notification:
let request = CNChangeHistoryFetchRequest()
request.startingToken = myToken
var error: NSError?
let fetchResult = myContactStore.swiftEnumerator(for: fetchRequest, error: &error)
myToken = myContactStore.currentHistoryToken // for next time
for event in fetchResult.value {
(event as! CNChangeHistoryEvent).accept(myCNChangeHistoryEventVisitor)
// or alternatively:
let newContact = (event as? CNChangeHistoryAddContactEvent).contact
}