34

iOS6のとメソッド ABAdressBookCreateWithOptionsを理解しようとしています。ABAddressBookRequestAccessWithCompletion

私が見つけたほとんどの情報は、「連絡先データへのアクセスを要求するには、ABAddressBookRequestAccessWithCompletion関数を呼び出した後に関数を呼び出すABAddressBookCreateWithOptions」です。

これらのメソッドを一緒に使用すると、アプリケーションが連絡先にアクセスできるようにするかどうかをユーザーに警告する必要があると思いますが、これらのメソッドを使用すると、プロンプトが表示されません。

誰かが実際の例でこれらのメソッドを一緒に呼び出す方法のサンプルコードを提供できますか?CFDictionary( )オプションを作成するにはどうすればよいですか?非推奨のABAddressBookCreateメソッドを使用して動作するコードがありますが、プライバシーの懸念に対応するためにiOS6に更新する必要があります。

ここで光を当てることができる人に事前に感謝します!

4

4 に答える 4

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...

}

これが誰かに役立つことを願っています...

于 2012-09-20T10:22:36.897 に答える
23

この質問に対して私が見たほとんどの回答は、GCDで非常に複雑なことを行い、メインスレッドをブロックすることになります。それは必要はありません!

これが私が使用しているソリューションです(iOS5とiOS6で動作します):

- (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);
}
于 2013-05-02T06:24:55.223 に答える
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];
          }
        }
      }
于 2012-12-14T21:33:23.527 に答える
2

これは元の質問と周辺的に関連していますが、他のどこにも言及されていないので、理解するのに約2日かかりました。アドレス帳の変更のコールバックを登録する場合は、メインスレッド上にある必要があります。

たとえば、このコードでは、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);
        });
    }
});
于 2014-02-05T19:37:17.490 に答える