Estoy tratando de entender la ABAdressBookCreateWithOptions y ABAddressBookRequestAccessWithCompletion métodos en iOS 6.

La mayoría de la información que he podido encontrar es la siguiente, «Para solicitar el acceso a los datos de contacto, llame a la ABAddressBookRequestAccessWithCompletion función después de llamar a la ABAddressBookCreateWithOptions función.»

Creo que juntos, estos métodos deben alertar al usuario decidir si desea permitir el acceso a las aplicaciones de contactos, sin embargo cuando yo los uso yo estoy viendo que no hay sistema.

Alguien podría proporcionar algún código de ejemplo de cómo estos métodos deben ser llamados a trabajar juntos en un ejemplo del mundo real? ¿Cómo puedo crear (CFDictionary) opciones? Tengo código de trabajo mediante el obsoleto ABAddressBookCreate método, pero sin necesidad de actualizar a iOS 6 para dar cabida a las preocupaciones de privacidad.

Gracias de antemano a cualquiera que pueda arrojar algo de luz aquí!

  • no. no. Esta pregunta debe ser eliminado de todos modos. o al menos modificado para mostrar la respuesta correcta.
InformationsquelleAutor codeqi | 2012-08-23

4 Comentarios

  1. 83

    Ahora que la CND ha sido levantado, he aquí mi solución para esto, por el donde se necesita reemplazar un método que devuelve una Matriz. (Si prefieres que no se bloque, mientras que el usuario es el que decide y está listo para potencialmente reescribir algunos de su código existente, por favor, mirar a David de la solución a continuación):

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

    Espero que esto ayude a alguien…

    • Usted no debería almacenar la accessGranted resultado en los valores predeterminados del usuario. El usuario puede cambiar el permiso en cualquier momento mediante la Configuración.app > Privacidad > Contactos. La documentación dice que «el usuario es El único que se le pide permiso la primera vez que se solicita el acceso. Las llamadas posteriores uso de la autorización conferida por el usuario.»
    • tienes razón, gracias 🙂
    • +1, Ya que mucha de la gente probablemente va a c&p (como hice yo), uno de los más mejora se debe reemplazar el ahora obsoleto ABAddressBookCreate con el ahora disponible ABAddressBookCreateWithOptions
    • Buen punto eladleb. Voy a actualizar el código tan pronto como puedo.
    • Por favor, tenga en cuenta que ABAddressBookCreateWithOptions sólo está disponible en IOS 6+.
    • Me gusta la idea de usar el semáforo a la fuerza ABAddressBookRequestAccessWithCompletion a ser modal, pero en mi dispositivo este código hace que la aplicación se bloquea, y sólo cuando la aplicación de las salidas que hace el ABAddressBookRequestAccessWithCompletion aparece el cuadro de diálogo. Alguien tiene alguna idea sobre cómo resolver eso?
    • que dispositivo y la versión de iOS que se encuentran en stdout? No se producen en nuestras pruebas en iOS 5.0+
    • La documentación en esto es horrible. El ABAdressBook docs no mencione que usted necesita para llamar a esta nueva API cuando se compila en ios6.
    • De acuerdo. La documentación es molesto. Gracias por el intel tho. Esto realmente ayudó.
    • La última declaración debe ser este: NSArray *thePeople = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);
    • gracias @abdie. acaba de agregar ARCO del puente…
    • por favor, dígame lo que va a ser la solución alternativa para iOS 5??? estoy trabajando en una versión más baja y este código mal tirar errores para mí. 🙁
    • no debería tirar errores si usted está construyendo en contra de la última SDK. Nuestra aplicación Ciclomotor es el uso de estos par de líneas, casi exactamente en la producción para el iOS5 y iOS6.
    • Ninguna razón específica de por qué usted no está utilizando CFRelease(libreta de direcciones)? Si yo uso esta sin liberar de la libreta de direcciones se las fugas, pero si puedo agregar CFRelease(libreta de direcciones) se bloquea. Alguien sabe por qué puede pasar?
    • dispatch_release está en desuso como de iOS6. No es necesario para iOS 6.0 o superior
    • Si el bloque de pasar a ABAddressBookRequestAccessWithCompletion llegar a poner en el hilo principal de la cola, luego no este código de resultado en un punto muerto? El hilo principal está esperando (que es una operación de bloqueo) para el semaphore a ser señalado, pero que no se señaló debido a la finalización del bloque que fue el lugar en la cola principal no se puede ejecutar porque el hilo principal está bloqueado…
    • Gracias! He utilizado este código en este gist: gist.github.com/dirtyhenry/7547064, que también presenta un ejemplo de cómo usar el resultante thePeople matriz

  2. 23

    La mayoría de las respuestas que he visto a esa pregunta, no loco complicado las cosas con DPC y terminan bloquear el hilo principal. No es necesario!

    Aquí está la solución que he estado usando (funciona en iOS 5 y 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);
    }
    • +1 para volver a la cola principal.
    • En todos los ejemplos que nadie se parece a la liberación de la libreta de direcciones no es necesario llamar a CFRelease(libreta de direcciones)?
    • Buen punto. He añadido algunos CFRelease declaraciones (no probado).
    • Creo que usted necesita para liberar el CFError si es no NULO de ABAddressBookCreateWithOptions. No estoy seguro acerca de la ABAddressBookRequestAccessWithCompletion caso.
    • Gran código. ¿Por qué es readAddressBookContacts un C-función, en lugar de un método como - (void)fetchContacts:...?
    • Cómo voy a ir yo acerca de la declaración de este? Cuando yo uso [auto fetchContracts:nil fracaso:nil]; va a ir a través de todo el código y luego no ir más lejos en el viewDidLoad, incluso he cambiado la terminación(contactos); finalización: (contactos); lo que tienes a la derecha de la final, pero no ir más lejos en el viewdidload, También por este código particular ¿cómo ir sobre la configuración de algunas variables de un viewcontroller.h, sólo se muestra como indefinido, incluso si yo uso [Viewcontroller prueba], oí el uso de variables globales funciona, pero quería ver si había una mejor manera

  3. 22

    Los otros de alto rango de la respuesta tiene problemas:

    • incondicional de llamadas a la API que no existen en iOS mayores de 6, por lo que el programa se bloquee en dispositivos antiguos.
    • bloquea el hilo principal, por lo que su aplicación no responde, y no avanza, durante el tiempo que el sistema de alertas de seguridad.

    Aquí está mi MRC toma en ella:

            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];
              }
            }
          }
    • ¿cuál será la solución alternativa para iOS 5.0?
    • Sorprendido de que esto no era upvoted a todos!!!! Estos son simples pero importantes mejoras en rendimiento y compatibilidad +1
    • PARA iOS5.0 o por debajo de, la aplicación debe proporcionar una costumbre mecanismo para tomar el consentimiento del usuario sobre el uso de la libreta de direcciones. La aplicación debe preguntar al usuario sobre el contacto de privacidad en el momento del lanzamiento. Debe ser el comportamiento similares a iOS6.0 no, símbolo debe ser sólo aparece una vez en la primera vez que lanza aplicación. Después de que la aplicación debe utilizar la persistió la configuración. Usted puede guardar la configuración en NSUserDefaults que es conveniente. Usted debe hacer un único método de utilidad que comprueba la versión de iOS y decidir de forma automática, si es iOS6.0 o por encima de uso ABAddressBookGetAuthorizationStatus() o bien utilizar la configuración personalizada.
    • Hola David, yo creo que son potencialmente escape de una CFErrorRef o dos allí. Si ABAddressBookCreateWithOptions la que te da un no-nil error, creo que es de su propiedad. No estoy realmente seguro de que en el caso de devolución de llamada (supongo no?) La documentación no está claro sobre cualquiera de ellos, pero la documentación en CFError.h definitivamente implica que usted es el dueño en el primer caso.
    • Gracias, dmaclach, tiene usted toda la razón. Al intentar editar y corregir.
  4. 2

    Esta es la zona periférica relacionada con la pregunta original, pero no he visto mencionado en otra parte, y me tomó casi dos días a la figura hacia fuera. Si usted se registra una devolución de llamada para la libreta de direcciones de cambios, DEBE estar en el hilo principal.

    Por ejemplo, en este código, sólo sync_address_book_two() nunca será llamado:

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

Dejar respuesta

Please enter your comment!
Please enter your name here