I am trying to understand the ABAdressBookCreateWithOptions
and ABAddressBookRequestAccessWithCompletion
methods in iOS 6.
我试图了解iOS 6中的ABAdressBookCreateWithOptions和ABAddressBookRequestAccessWithCompletion方法。
The most information i have been able to find is the following, "To request access to contact data, call the ABAddressBookRequestAccessWithCompletion
function after calling the ABAddressBookCreateWithOptions
function."
我能够找到的最多信息如下:“要请求访问联系人数据,请在调用ABAddressBookCreateWithOptions函数后调用ABAddressBookRequestAccessWithCompletion函数。”
I believe together these methods should alert the user to decide whether to allow the application access to contacts, however when I use them I am seeing no prompt.
我相信这些方法应该提醒用户决定是否允许应用程序访问联系人,但是当我使用它们时,我看不到任何提示。
Could someone provide some sample code of how these methods should be called together in a real world example? How do I create (CFDictionary
) options? I have working code using the deprecated ABAddressBookCreate
method, but need to update to iOS 6 to accommodate privacy concerns.
有人可以提供一些示例代码,说明如何在现实世界的例子中一起调用这些方法吗?如何创建(CFDictionary)选项?我使用不推荐的ABAddressBookCreate方法工作代码,但需要更新到iOS 6以适应隐私问题。
Thanks in advance to anyone who can shed some light here!
在此感谢任何能够解决问题的人!
4 个解决方案
#1
83
Now that the NDA has been lifted, here is my solution for this for the where you need replace a method which returns an Array. (If you'd rather not block while the user is deciding and are ready to potentially rewrite some of your existing code, please look at David's solution below):
既然已经解除了NDA,那么我的解决方案就是你需要的地方替换一个返回一个数组的方法。 (如果您在用户决定并且准备好重写某些现有代码时不想阻止,请查看下面的David的解决方案):
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...
}
Hope this helps somebody...
希望这有助于某人......
#2
23
Most answers I've seen to this question do crazy complicated things with GCD and end up blocking the main thread. It's not necessary!
我在这个问题上看到的大多数答案使用GCD做了疯狂复杂的事情并最终阻止了主线程。这不是必需的!
Here's the solution I've been using (works on iOS 5 and iOS 6):
这是我一直在使用的解决方案(适用于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);
}
#3
22
The other high ranking answer has problems:
另一个高排名答案有问题:
- it unconditionally calls API that don't exist in iOS older than 6, so your program will crash on old devices.
- 它无条件地调用在6岁以上的iOS中不存在的API,因此您的程序将在旧设备上崩溃。
- it blocks the main thread, so your app is unresponsive, and not making progress, during the time the system alert s up.
- 它会阻止主线程,因此在系统警报启动期间,您的应用程序无响应,无法取得进展。
Here's my MRC take on it:
这是我对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];
}
}
}
#4
2
This is peripherally related to the original question, but I have not seen it mentioned anywhere else, and it took me about two days to figure it out. If you register a callback for address book changes, it MUST be on the main thread.
这与原始问题在外围有关,但我还没有看到它在其他任何地方提到过,我花了两天时间来弄明白。如果为地址簿更改注册回调,则必须在主线程上。
For example, in this code, only sync_address_book_two() will ever be called:
例如,在此代码中,只会调用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);
});
}
});
#1
83
Now that the NDA has been lifted, here is my solution for this for the where you need replace a method which returns an Array. (If you'd rather not block while the user is deciding and are ready to potentially rewrite some of your existing code, please look at David's solution below):
既然已经解除了NDA,那么我的解决方案就是你需要的地方替换一个返回一个数组的方法。 (如果您在用户决定并且准备好重写某些现有代码时不想阻止,请查看下面的David的解决方案):
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...
}
Hope this helps somebody...
希望这有助于某人......
#2
23
Most answers I've seen to this question do crazy complicated things with GCD and end up blocking the main thread. It's not necessary!
我在这个问题上看到的大多数答案使用GCD做了疯狂复杂的事情并最终阻止了主线程。这不是必需的!
Here's the solution I've been using (works on iOS 5 and iOS 6):
这是我一直在使用的解决方案(适用于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);
}
#3
22
The other high ranking answer has problems:
另一个高排名答案有问题:
- it unconditionally calls API that don't exist in iOS older than 6, so your program will crash on old devices.
- 它无条件地调用在6岁以上的iOS中不存在的API,因此您的程序将在旧设备上崩溃。
- it blocks the main thread, so your app is unresponsive, and not making progress, during the time the system alert s up.
- 它会阻止主线程,因此在系统警报启动期间,您的应用程序无响应,无法取得进展。
Here's my MRC take on it:
这是我对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];
}
}
}
#4
2
This is peripherally related to the original question, but I have not seen it mentioned anywhere else, and it took me about two days to figure it out. If you register a callback for address book changes, it MUST be on the main thread.
这与原始问题在外围有关,但我还没有看到它在其他任何地方提到过,我花了两天时间来弄明白。如果为地址簿更改注册回调,则必须在主线程上。
For example, in this code, only sync_address_book_two() will ever be called:
例如,在此代码中,只会调用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);
});
}
});