如何在iOS 6中正确使用ABAddressBookCreateWithOptions方法?
我想了解iOS 6中的ABAdressBookCreateWithOptions
和ABAddressBookRequestAccessWithCompletion
方法。
我所能find的最多信息如下:“要请求访问联系人数据,请在调用ABAddressBookRequestAccessWithCompletion
函数后调用ABAddressBookCreateWithOptions
函数”。
我相信这些方法应该提醒用户决定是否允许应用程序访问联系人,但是当我使用它们时,我看不到任何提示。
有人可以提供一些示例代码,说明如何在真实世界的例子中一起调用这些方法吗? 我如何创build( CFDictionary
)选项? 我有工作代码使用不推荐的ABAddressBookCreate
方法,但需要更新到iOS 6以适应隐私问题。
在此先感谢任何可以在这里点亮的人!
现在NDA已经解除了,这里是我的解决scheme,你需要replace返回一个Array的方法。 (如果您不想在用户决定并准备好重写某些现有代码时进行阻止,请参阅以下David的解决scheme):
ABAddressBookRef addressBook = ABAddressBookCreate(); __block BOOL accessGranted = NO; if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6 dispatch_semaphore_t sema = dispatch_semaphore_create(0); ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) { accessGranted = granted; dispatch_semaphore_signal(sema); }); dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); dispatch_release(sema); } else { // we're on iOS 5 or older accessGranted = YES; } if (accessGranted) { NSArray *thePeople = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook); // Do whatever you need with thePeople... }
希望这可以帮助别人…
我在这个问题上看到的大部分答案都会让GCD变得非常复杂,并最终阻止主线程 。 这不是必需的!
这是我一直在使用的解决scheme(适用于iOS 5和iOS 6):
- (void)fetchContacts:(void (^)(NSArray *contacts))success failure:(void (^)(NSError *error))failure { if (ABAddressBookRequestAccessWithCompletion) { // on iOS 6 CFErrorRef err; ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &err); if (err) { // handle error CFRelease(err); return; } ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) { // ABAddressBook doesn't gaurantee execution of this block on main thread, but we want our callbacks to be dispatch_async(dispatch_get_main_queue(), ^{ if (!granted) { failure((__bridge NSError *)error); } else { readAddressBookContacts(addressBook, success); } CFRelease(addressBook); }); }); } else { // on iOS < 6 ABAddressBookRef addressBook = ABAddressBookCreate(); readAddressBookContacts(addressBook, success); CFRelease(addressBook); } } static void readAddressBookContacts(ABAddressBookRef addressBook, void (^completion)(NSArray *contacts)) { // do stuff with addressBook NSArray *contacts = @[]; completion(contacts); }
其他高排名的答案有问题:
- 它会无条件地调用iOS 6以前不存在的API,因此您的程序会在旧设备上崩溃。
- 它会阻塞主线程,所以在系统警报发生的时候,你的应用程序没有反应,而且没有进展。
这是我的MRC采取的:
ABAddressBookRef ab = NULL; // ABAddressBookCreateWithOptions is iOS 6 and up. if (&ABAddressBookCreateWithOptions) { NSError *error = nil; ab = ABAddressBookCreateWithOptions(NULL, (CFErrorRef *)&error); #if DEBUG if (error) { NSLog(@"%@", error); } #endif if (error) { CFRelease((CFErrorRef *) error); error = nil; } } if (ab == NULL) { ab = ABAddressBookCreate(); } if (ab) { // ABAddressBookRequestAccessWithCompletion is iOS 6 and up. if (&ABAddressBookRequestAccessWithCompletion) { ABAddressBookRequestAccessWithCompletion(ab, ^(bool granted, CFErrorRef error) { if (granted) { // constructInThread: will CFRelease ab. [NSThread detachNewThreadSelector:@selector(constructInThread:) toTarget:self withObject:ab]; } else { CFRelease(ab); // Ignore the error } // CFErrorRef should be owned by caller, so don't Release it. }); } else { // constructInThread: will CFRelease ab. [NSThread detachNewThreadSelector:@selector(constructInThread:) toTarget:self withObject:ab]; } } }
这与原来的问题是有关系的,但是在其他地方我没有看到,所以花了我两天的时间才弄明白。 如果你注册了地址簿更改的callback,它必须在主线程上。
例如,在这段代码中,只有sync_address_book_two()会被调用:
ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool granted, CFErrorRef error) { if (granted) { ABAddressBookRegisterExternalChangeCallback (_addressBook, sync_address_book_one, NULL); dispatch_async(dispatch_get_main_queue(), ^{ ABAddressBookRegisterExternalChangeCallback (_addressBook, sync_address_book_two, NULL); }); } });