使用ABAddressBookRegisterExternalChangeCallback或任何其他通知获取已删除的联系人

时间:2015-09-08 12:20:41

标签: ios objective-c addressbook abaddressbook abaddressbooksource

关于收听iOS地址簿更改回调,提出了许多SO问题。着名问题Address book sync

但我的问题很狭窄,即如何在地址簿同步回调期间删除哪些联系人被删除。

void MyAddressBookExternalChangeCallback (ABAddressBookRef ntificationaddressbook,CFDictionaryRef info,void *context)
{
 NSLog(@"Changed Detected......");
 /*
   NSDate *lastSyncTime = [self gettingLastSyncTime];
   // By above time, I could get which contacts are modified(kABPersonModificationDateProperty)
   // and which contacts are created newly( ABRecordGetRecordID()
   // But how can I get this contact was DELETED?
 */
}

但是有人在Detect what was changed....中清除了这个问题。在这里,他们做了(a)第一次存储所有记录ID(b)在同步期间,检查所有存储的记录ID与当前地址簿ID,以检查它们是否可用。如果没有,则假设它被删除联系(代价高昂的操作)。

我的问题:还有其他方法可以检测DELETED联系人吗?

1 个答案:

答案 0 :(得分:1)

据我所知,唯一的方法是以某种方式存储联系人ID并检查已修改,因为MyAddressBookExternalChangeCallback仅在您的应用处于活动状态或后台时调用,因此当您的应用是终止,您将无法跟踪这些更改。这是我的地址簿同步控制器的实现,它只是与设备保持最新的本地联系人:

// Header.h
@interface AddressBookSyncController : NSObject

+ (instancetype)sharedInstance;
+ (NSString *)archiveFilePath;

- (void)syncAddressBook;
- (void)beginObserving;
- (void)invalidateObserving;

@end

// Implementation.m
NSString *const AddressBookSyncAllKey = @"AddressBookSyncAllKey";

@interface AddressBookSyncController ()

@property (nonatomic) ABAddressBookRef addressBook;
@property (nonatomic) BOOL isObserving;

@end

@implementation AddressBookSyncController

+ (instancetype)sharedInstance
{
    static AddressBookSyncController *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [AddressBookSyncController new];
    });

    return instance;
}

+ (NSString *)archiveFilePath
{
    // Your archive file path...
}

- (void)syncAddressBook
{
    dispatch_async(dispatch_get_main_queue(), ^{
        if (ABAddressBookGetAuthorizationStatus() != kABAuthorizationStatusAuthorized) return;

        NSString *archiveFilePath = [AddressBookSyncController archiveFilePath];
        BOOL needSyncAllContacts = [[[NSUserDefaults standardUserDefaults] objectForKey:AddressBookSyncAllKey] boolValue];
        BOOL archiveExists = [[NSFileManager defaultManager] fileExistsAtPath:archiveFilePath];

        if (!needSyncAllContacts && !archiveExists) return;

        ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
        CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
        NSInteger nPeople = ABAddressBookGetPersonCount(addressBook);
        NSMutableArray *syncContacts = [NSMutableArray arrayWithCapacity:nPeople];
        NSMutableArray *archivedContacts = archiveExists ? [[NSKeyedUnarchiver unarchiveObjectWithFile:archiveFilePath] mutableCopy] : [NSMutableArray array];

        if (needSyncAllContacts)
        {
            NSMutableArray *newContacts = [NSMutableArray array];

            for (NSInteger i = 0; i < nPeople; ++i)
            {
                ABRecordRef record = CFArrayGetValueAtIndex(allPeople, i);
                NSInteger recordId = ABRecordGetRecordID(record);
                NSDate *modificationDate = (__bridge_transfer NSDate *)ABRecordCopyValue(record, kABPersonModificationDateProperty);
                AddressContact *contact = [[archivedContacts filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"recordId == %i", recordId]] firstObject];

                if (contact && [contact.modificationDate isEqualToDate:modificationDate]) continue;

                AddressContact *newContact = [AddressContact addressContactWithRecord:record];

                [syncContacts addObject:newContact];

                if (contact)
                {
                    NSUInteger idx = [archivedContacts indexOfObject:contact];
                    if (idx != NSNotFound)
                    {
                        [archivedContacts replaceObjectAtIndex:idx withObject:newContact];
                    }
                    else
                    {
                        NSLog(nil, @"idx == NSNotFound for syncAddressBook");
                    }
                }
                else
                {
                    [newContacts addObject:newContact];
                }
            }

            [archivedContacts addObjectsFromArray:newContacts];
        }
        else
        {
            for (NSInteger i = 0, l = archivedContacts.count; i < l; ++i)
            {
                AddressContact *contact = [archivedContacts objectAtIndex:i];
                ABRecordRef record = ABAddressBookGetPersonWithRecordID(addressBook, (int)contact.recordId);

                if (!record) continue;

                NSDate *modificationDate = (__bridge_transfer NSDate *)ABRecordCopyValue(record, kABPersonModificationDateProperty);

                if ([modificationDate isEqualToDate:contact.modificationDate]) continue;

                AddressContact *newContact = [AddressContact addressContactWithRecord:record];

                [syncContacts addObject:newContact];
                [archivedContacts replaceObjectAtIndex:i withObject:newContact];
            }
        }

        CFRelease(allPeople);
        CFRelease(addressBook);

        BOOL result = NO;

        if ([syncContacts count] != 0)
        {
            // Do your logic with contacts
           result = [NSKeyedArchiver archiveRootObject:archivedContacts toFile:archiveFilePath];
        }

        NSLog(@"Archiving %@", result ? @"Succeed" : @"Failed");

        [[NSUserDefaults standardUserDefaults] setObject:@(NO) forKey:AddressBookSyncAllKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
    });
}

- (void)beginObserving
{
    if (self.isObserving || ABAddressBookGetAuthorizationStatus() != kABAuthorizationStatusAuthorized) return;

    self.addressBook = ABAddressBookCreateWithOptions(NULL, nil);
    ABAddressBookRegisterExternalChangeCallback(self.addressBook, addressBookChanged, (__bridge void *)self);

    self.isObserving = YES;
}

- (void)invalidateObserving
{
    if (!self.isObserving) return;

    ABAddressBookUnregisterExternalChangeCallback(self.addressBook, addressBookChanged, (__bridge void *)self);
    CFRelease(self.addressBook);

    self.isObserving = NO;
}

void addressBookChanged(ABAddressBookRef reference, CFDictionaryRef dictionary, void *context)
{
    NSLog(@"%@ changed %@", reference, dictionary);

    ABAddressBookRevert(reference);

    [(__bridge AddressBookSyncController *)context syncAddressBook];
}

@end