Быстрые вложенные необязательные типы значений (структуры) и модификация свойств - PullRequest
0 голосов
/ 16 мая 2018

Я использую несколько типов значений в моей модели, и эти типы значений (структуры) имеют свойства, вложенные в другие типы значений (структуры).Затем, имея корневой объект, я хочу изменить (добавить, удалить, обновить) свойство в этой вложенной структуре внутри другой структуры.Кроме того, эти свойства обычно имеют необязательные типы и могут быть нулевыми.Так как тип значения при присваивании var, let копируется, я не могу использовать необязательный bing этого экземпляра внутренних структур и изменение их позже.Таким образом, единственный способ, которым я должен сделать эту модификацию, выглядит следующим образом:

if let cleaningDetails = initialPackage?.cleaningsSchedule?.details?[indexPath.row], cleaningDetails.startTimes == nil {
                        initialPackage?.cleaningsSchedule?.details?[indexPath.row].startTimes = []
                    }

Так что это действительно единственный вариант при использовании типов значений.Какие есть другие решения?Переход на классы (ссылочные типы) - вот это функциональное программирование типа значения действительно так здорово?Или я должен просто использовать больше мутирующих функций в этой структуре для облегчения изменений?

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Во-первых, вы должны уменьшить количество дополнительных в системе. Существуют различные способы работы с коллекциями Optional (например, мутирующие вспомогательные методы, как вы предлагали), но чрезмерное использование Optional создает много ненужной сложности. Очень редко, чтобы Коллекция любого вида была Факультативной. Это имеет смысл, только если nil и «пустой» означают разные вещи (и это очень редко).

Вместо того, чтобы обернуть всю модель данных вокруг определенного JSON API, преобразуйте JSON в нужную модель данных. Например, вот модель JSON, которая включает требуемый тип Int и может включать или не включать массив, но внутренне мы хотим рассматривать «отсутствующий массив» как «пустой». Мы также хотим удалить пустые массивы перед их отправкой.

import Foundation

let json = Data("""
{
    "y": 1
}
""".utf8)

struct X {
    var y: Int
    var z: [String]
}

extension X: Codable {
    enum CodingKeys: String, CodingKey {
        case y, z
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        y = try container.decode(Int.self, forKey: .y)
        z = try container.decodeIfPresent([String].self, forKey: .z) ?? []
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(y, forKey: .y)
        if !z.isEmpty {
            try container.encode(z, forKey: .z)
        }
    }
}

let decoder = JSONDecoder()
print(try decoder.decode(X.self, from: json))

let encoder = JSONEncoder()
print(String(data: try encoder.encode(X(y: 1, z: [])), encoding: .utf8)!)

Это перемещает всю работу в два метода, а не распределяет ее по всей программе каждый раз, когда вы получаете доступ к модели данных. Настраиваемые кодируемые файлы по-прежнему немного утомительны для написания (и могут содержать незначительные ошибки на этапе кодирования), поэтому, если у вас их много, вам следует обратиться к SwiftGen , который может написать их для вас.

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

struct X: Codable{
    enum CodingKeys: String, CodingKey {
        case y
        case _z = "z"
    }

    var y: Int
    private var _z: [String]?  // The actual `z` we got from the JSON
    var z: [String] { get { return _z ?? [] } set { _z = newValue } }

    init(y: Int, z: [String]?) {
        self.y = y
        self._z = z
    }
}

«Реальный» z хранится в _z и доступен для повторной сериализации, но остальная часть программы никогда не видит Необязательный.

Еще один довольно распространенный метод - создать слой адаптера, который преобразует «JSON-совместимую» структуру во внутреннюю модель данных и обратно. Это позволяет вашей внутренней модели данных немного отличаться от JSON, если это удобно.

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

0 голосов
/ 16 мая 2018

Вы всегда можете также указать начальное значение для опциональных параметров, просто попробуйте объявить массив как var array: [Element]? = [], чтобы вам не приходилось проверять нулевое значение.Вы можете использовать динамическое связывание для использования других условий

...