如何在iOS 6中正确使用ABAddressBookCreateWithOptions方法?

cod*_*eqi 34 iphone cocoa-touch objective-c abaddressbook ios

我试图了解iOS 6中的方法ABAdressBookCreateWithOptionsABAddressBookRequestAccessWithCompletion方法.

我能够找到的最多信息如下:"要请求访问联系人数据,请在调用该ABAddressBookRequestAccessWithCompletion函数后调用该ABAddressBookCreateWithOptions函数."

我相信这些方法应该提醒用户决定是否允许应用程序访问联系人,但是当我使用它们时,我看不到任何提示.

有人可以提供一些示例代码,说明如何在现实世界的例子中一起调用这些方法吗?我如何创建(CFDictionary)选项?我使用已弃用的ABAddressBookCreate方法使用代码,但需要更新到iOS 6以适应隐私问题.

在此先感谢任何可以解决问题的人!

Eng*_*epe 83

既然已经取消了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...

}
Run Code Online (Sandbox Code Playgroud)

希望这有助于某人......

  • +1因为很多人可能会c&p(就像我一样),还有一个改进应该是用现在可用的ABAddressBookCreateWithOptions替换现已弃用的ABAddressBookCreate (5认同)
  • 您不应将accessGranted结果存储在用户默认值中.用户可以通过进入Settings.app>隐私>联系人随时更改权限.文档说:"只有在您第一次请求访问权限时才会要求用户提供权限.以后的调用会使用用户授予的权限." (4认同)
  • 请注意,ABAddressBookCreateWithOptions仅在IOS 6+中可用. (3认同)
  • 最后一句话应该是这样的:`NSArray*thePeople =(__ bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);` (3认同)

Nik*_*Nik 23

我在这个问题上看到的大多数答案使用GCD做了疯狂复杂的事情并最终阻止了主线程.这不是必需的!

这是我一直在使用的解决方案(适用于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);
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*ter 22

另一个高排名答案有问题:

  • 它无条件地调用在6岁以上的iOS中不存在的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];
          }
        }
      }
Run Code Online (Sandbox Code Playgroud)