Swift - проверка свойства одиночного значения неуправляемой адресной книги для nil
Я отношусь к iOS-Development и быстрому. Но до этого момента я всегда мог помочь себе в некоторых исследованиях в stackoverflow и нескольких документах и учебниках.
Однако есть проблема, которую я пока не нашел.
Я хочу получить некоторые данные из адресной книги пользователей (например, свойство одиночного значения kABPersonFirstNameProperty
). Поскольку функция .takeRetainedValue()
вызывает ошибку, если этот контакт не имеет значения firstName в адресной книге, мне нужно убедиться, что функция ABRecordCopyValue()
возвращает значение. Я попытался проверить это в закрытии:
let contactFirstName: String = {
if (ABRecordCopyValue(self.contactReference, kABPersonFirstNameProperty) != nil) {
return ABRecordCopyValue(self.contactReference, kABPersonFirstNameProperty).takeRetainedValue() as String
} else {
return ""
}
}()
contactReference
- это переменная типа ABRecordRef!
Когда контакт адресной книги предоставляет значение firstName, все работает нормально. Но если firstName отсутствует, приложение вылетает с помощью функции .takeRetainedValue()
. Кажется, что инструкция if не помогает, потому что неуправляемое возвращаемое значение функции ABRecordCopyValue()
не равно нулю, хотя firstName отсутствует.
Надеюсь, я смог сделать свою проблему понятной. Было бы здорово, если бы кто-нибудь мог помочь мне с некоторой мозговой волной.
Ответы
Ответ 1
Если мне нужны значения, связанные с различными свойствами, я использую следующий синтаксис:
let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String
let last = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String
Или вы можете использовать необязательную привязку:
if let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String {
// use `first` here
}
if let last = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String {
// use `last` here
}
Если вы действительно хотите вернуть необязательный параметр, где отсутствующее значение представляет собой строку с нулевой длиной, вы можете использовать оператор ??
:
let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String ?? ""
let last = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String ?? ""
Ответ 2
Я воспользовался этой функцией здесь:
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
}
Вы можете использовать его в своем свойстве:
let contactFirstName: String = {
if let firstName: String = rawValueFromABRecordRef(recordRef, forProperty: kABPersonFirstNameProperty) {
return firstName
}
else {
return ""
}
}()
Ответ 3
Возможно, это больше, чем просто ответ на ваш вопрос, но именно так я занимаюсь с адресной книгой.
Я определил пользовательский оператор:
infix operator >>> { associativity left }
func >>> <T, V> (lhs: T, rhs: T -> V) -> V {
return rhs(lhs)
}
позволяет связывать несколько вызовов с функциями более читаемым способом, например:
funcA(funcB(param))
становится
param >>> funcB >>> funcA
Затем я использую эту функцию для преобразования Unmanaged<T>
в быстрый тип:
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
}
и аналог, работающий с 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
}
и это код, который открывает адресную книгу, извлекает все контакты и для каждого читает имя и организацию (в симуляторе firstName всегда имеет значение, а у отдела нет, поэтому это хорошо для тестирования):
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)")
}
}
Обратите внимание, что оператор println
печатает необязательный, поэтому вы увидите в консоли Optional("David")
вместо David
. Конечно, это только для демонстрации.
Функция, отвечающая на ваш вопрос, extractUnmanaged
, которая принимает необязательный неуправляемый, разворачивает его, извлекает сохраненное значение как необязательное, разворачивает его и в конце пытается применить к целевому типу, который равен String
для свойства имени. Тип inferral заботится о том, какие T и V: T - тип, завернутый в Unmanaged
, V - тип возвращаемого значения, который известен, поскольку указан при объявлении целевой переменной let firstName: String? = ...
.
Предполагаю, что вы уже позаботились о том, чтобы проверить и попросить пользователя получить доступ к адресной книге.