Swift - проверка однозначного свойства неуправляемой адресной книги на ноль

Я относительно новичок в iOS-разработке и Swift. Но до этого момента я всегда был в состоянии помочь себе с некоторыми исследованиями стекового потока и несколькими документациями и учебными пособиями. Тем не менее, есть проблема, которую я не смог найти никакого решения.

Я хочу получить некоторые данные из адресной книги пользователя (например, свойство с одним значением 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() функция не ноль, хотя имя не указано.

Я надеюсь, что смог прояснить мою проблему. Было бы здорово, если бы кто-нибудь помог мне с мозговыми волнами.

3 ответа

Решение

Если мне нужны значения, связанные с различными свойствами, я использую следующий синтаксис:

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 ?? ""

Я получил это через эту функцию здесь:

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 ""
    }
}()

Может быть, это больше, чем просто ответ на ваш вопрос, но так я поступаю с адресной книгой.

Я определил пользовательский оператор:

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? = ...,

Я предполагаю, что вы уже позаботились о проверке и запросе у пользователя разрешения на доступ к адресной книге.

Другие вопросы по тегам