Swift: comprobación de la propiedad de valor único de la libreta de direcciones no administrada por cero

Soy relativamente nuevo en iOS-Development y rápido. Pero hasta este punto, siempre fui capaz de ayudarme a mí mismo con algunas investigaciones sobre stackoverflow y varias documentaciones y tutoriales. Sin embargo, hay un problema que no pude encontrar ninguna solución todavía.

Quiero get algunos datos de la libreta de direcciones de los usuarios (por ejemplo, la propiedad de valor único kABPersonFirstNameProperty ). Debido a que la function .takeRetainedValue() un error si este contacto no tiene un valor firstName en la libreta de direcciones, debo asegurarme de que la function ABRecordCopyValue() devuelva un valor. Traté de verificar esto en un cierre:

 let contactFirstName: String = { if (ABRecordCopyValue(self.contactReference, kABPersonFirstNameProperty) != nil) { return ABRecordCopyValue(self.contactReference, kABPersonFirstNameProperty).takeRetainedValue() as String } else { return "" } }() 

contactReference es una variable de tipo ABRecordRef!

Cuando un contacto de libreta de direcciones proporciona un valor firstName, todo funciona bien. Pero si no hay firstName, la aplicación se bloquea por la function .takeRetainedValue() . Parece que la instrucción if no ayuda porque el valor de retorno no administrado de la function ABRecordCopyValue() no es nil aunque no hay ningún primer nombre.

Espero haber podido aclarar mi problema. Sería genial si alguien me pudiera ayudar con alguna idea cerebral.

Si quiero los valores asociados con varias properties, utilizo la siguiente syntax:

 let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String let last = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String 

O puede utilizar el enlace opcional:

 if let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String { // use `first` here } if let last = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String { // use `last` here } 

Si realmente desea devolver un valor no opcional, donde el valor faltante es una cadena de longitud cero, puede usar ?? operador:

 let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String ?? "" let last = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String ?? "" 

Lo conseguí a través de esta function aquí:

 func rawValueFromABRecordRef<T>(recordRef: ABRecordRef, forProperty property: ABPropertyID) -> T? { var returnObject: T? = nil if let rawValue: Unmanaged<AnyObject>? = ABRecordCopyValue(recordRef, property) { if let unwrappedValue: AnyObject = rawValue?.takeRetainedValue() { println("Passed: \(property)") returnObject = unwrappedValue as? T } else { println("Failed: \(property)") } } return returnObject } 

Puedes usarlo en tu propiedad así:

 let contactFirstName: String = { if let firstName: String = rawValueFromABRecordRef(recordRef, forProperty: kABPersonFirstNameProperty) { return firstName } else { return "" } }() 

Tal vez sea más que solo responder a su pregunta, pero así es como trato con la libreta de direcciones.

He definido un operador personalizado:

 infix operator >>> { associativity left } func >>> <T, V> (lhs: T, rhs: T -> V) -> V { return rhs(lhs) } 

permitiendo encadenar múltiples llamadas a funciones de una manera más legible, por ejemplo:

 funcA(funcB(param)) 

se convierte

 param >>> funcB >>> funcA 

Luego utilizo esta function para convertir un Unmanaged<T> en un tipo rápido:

 func extractUnmanaged<T, V>(value: Unmanaged<T>?) -> V? { if let value = value { var innerValue: T? = value.takeRetainedValue() if let innerValue: T = innerValue { return innerValue as? V } } return .None } 

y una contraparte que trabaja con CFArray :

 func extractUnmanaged(value: Unmanaged<CFArray>?) -> [AnyObject]? { if let value = value { var innerValue: CFArray? = value.takeRetainedValue() if let innerValue: CFArray = innerValue { return innerValue.__conversion() } } return .None } 

y este es el código para abrir la libreta de direcciones, recuperar todos los contactos y, para cada uno, leer el nombre y la organización (en el simulador, el primer nombre siempre tiene un valor, mientras que el departamento no, por lo que es bueno para las testings):

 let addressBook: ABRecordRef? = ABAddressBookCreateWithOptions(nil, nil) >>> extractUnmanaged let results = ABAddressBookCopyArrayOfAllPeople(addressBook) >>> extractUnmanaged if let results = results { for result in results { let firstName: String? = (result, kABPersonFirstNameProperty) >>> ABRecordCopyValue >>> extractUnmanaged let organization: String? = (result, kABPersonOrganizationProperty) >>> ABRecordCopyValue >>> extractUnmanaged println("\(firstName) - \(organization)") } } 

Tenga en count que la statement println imprime el opcional, por lo que verá en la console Optional("David") lugar de solo David . Por supuesto, esto es solo para demostración.

La function que responde a su pregunta es extractUnmanaged , que toma un opcional no administrado, lo desenvuelve, recupera el valor retenido como opcional, lo desenvuelve y al final intenta una conversión al tipo de destino, que es String para la propiedad del primer nombre. Type inferral se encarga de averiguar qué son T y V: T es el tipo envuelto en Unmanaged , V es el tipo de retorno, que se conoce porque se especifica cuando se declara la variable de destino, se let firstName: String? = ... let firstName: String? = ...

Supongo que ya se ha ocupado de verificar y pedir permiso al usuario para acceder a la libreta de direcciones.