34 votes

Comment utiliser correctement la méthode ABAddressBookCreateWithOptions dans iOS 6?

Je suis en train d'essayer de comprendre le ABAdressBookCreateWithOptions et ABAddressBookRequestAccessWithCompletion méthodes dans iOS 6.

La plupart des informations que j'ai pu trouver est la suivante, "pour demander l'accès À des données de contact, appelez le ABAddressBookRequestAccessWithCompletion fonction après l'appel de la ABAddressBookCreateWithOptions fonction."

Je crois que l'ensemble de ces méthodes doit alerter l'utilisateur de décider s'il faut autoriser l'application à accéder aux contacts, cependant lorsque je les utilise, je vois pas d'invite.

Quelqu'un pourrait-il fournir un exemple de code de ces méthodes doit être convoquée dans un monde réel exemple? Comment puis-je créer (CFDictionary) options? J'ai du code qui fonctionne à l'aide de l'obsolète ABAddressBookCreate méthode, mais besoin de mettre à jour vers iOS 6 pour accueillir des problèmes de confidentialité.

Merci d'avance à tous ceux qui peuvent jeter un peu de lumière ici!

83voto

Engin Kurutepe Points 3673

Maintenant que la NDA a été levée, voici ma solution: remplacer la méthode qui renvoie un tableau. (Si vous préférez ne pas bloquer pendant que l'utilisateur décide et est prêt à potentiellement réécrire une partie de votre code existant, veuillez consulter la solution de David ci-dessous):

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

}
 

J'espère que ça aide quelqu'un ...

23voto

Nik Points 2505

La plupart des réponses que j'ai vues à cette question font des choses folles et compliquées avec GCD et finissent par bloquer le fil principal . Ce n'est pas nécessaire!

Voici la solution que j'utilise (fonctionne sur iOS 5 et 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);
}
 

22voto

DavidPhillipOster Points 1294

L'autre réponse de haut niveau a des problèmes:

  • il appelle inconditionnellement des API qui n'existent pas dans iOS de plus de 6 ans, de sorte que votre programme se bloque sur les anciens appareils.
  • il bloque le fil principal, de sorte que votre application ne répond pas et ne progresse pas pendant le temps que l'alerte système se déclenche.

Voici ce que ma MRC prend à ce sujet:

         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];
          }
        }
      }
 

2voto

Eli Burke Points 422

Ceci est indirectement lié à la question initiale, mais je ne l'ai pas vue mentionnée ailleurs, et il m'a fallu environ deux jours pour le résoudre. Si vous enregistrez un rappel pour les changements de carnet d'adresses, il DOIT être sur le fil principal.

Par exemple, dans ce code, seul sync_address_book_two () sera appelé:

 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);
        });
    }
});
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X