Я слежу за видео на YouTube-канале Firebase. Начиная примерно с 27:45, инструктор пытается установить переменную на основе логического значения и в итоге получает следующий код в init(task: Task):
$task
.map { task in
task.isCompleted ? "checkmark.circle.fill" : "circle"
}
.assign(to: \.completionStateIconName, on: self)
.store(in: &cancellables)
Мне это кажется слишком запутанным. Во-первых, я не могу найти документацию по использованию .map для объекта структуры, только для массивов и т. д. Во-вторых, что с этим &cancellables? (Он определяется как private var cancellables = Set<AnyCancellable>() до init{}.) В-третьих, почему весь этот код, а не просто:
task.completionStateIconName = task.isCompleted ? "checkmark.circle.fill" : "circle"
Кажется, что это дает тот же результат, но будет ли что-то в дальнейшем, что первый фрагмент кода работает, а второй - нет?





$task (с префиксом $) является прогнозируемым значением оболочки свойства @Published и возвращает переменную типа Published.Publisher. Другими словами, это издатель Combine, который публикует значение всякий раз, когда свойство - в данном случае Task - изменяется.
Если вы не узнали о фреймворке Combine (или других реактивных фреймворках), этого ответа определенно будет недостаточно. На высоком уровне издатель Combine генерирует значения, которые вы можете преобразовать с помощью операторов, таких как .map, и в конечном итоге подписаться на них, например, с помощью .sink или .assign.
Итак, построчно:
// a publisher of Task values
$task
// next, transform Task into a String using its isCompleted property
.map { task in
task.isCompleted ? "circle.fill" : "circle"
}
// subscribe, by assigning the String value to the completionStateIconName prop
.assign(to: \.completionStateIconName, on: self)
Теперь приведенное выше возвращает экземпляр AnyCancellable, который вам нужно сохранить, пока вы хотите получать значения. Таким образом, вам нужно либо сохранить его непосредственно как свойство, либо использовать .store, чтобы добавить его в Set<AnyCancellable> - распространенный подход.
Итак, почему это так запутано? Предположительно, это построено так, что если свойство task когда-либо изменится, конвейер Combine обновит свойство completionStateIconName.
Если вы только что сделали:
completionStateIconName = task.isCompleted ? "circle.fill" : "circle"
это присвоило бы значение только в начале.
При этом в данном конкретном случае использование Combine может быть излишне запутанным, в то время как использование просто didSet:
var task: Task {
didSet {
completionStateIconName ? task.isCompleted ? "circle.fill" : "circle"
}
}
@robmayoff, я не смотрел видео полностью, но да - похоже, что использование Combine в этом видео является ненужным излишеством, и поскольку задача - это @Published, просто использование вычисляемого свойства будет работать, если оно используется представлением.
@NewDev @rob Спасибо. Я забыл предложить однострочник как часть didSet или вычисляемого значения, но на самом деле я думал именно об этом. Спасибо, что нашли время, чтобы ответить так подробно. Я знаю из Combine, но точно не знать. @rob Я думаю, у вас есть лишний кусок в вашем троичном выражении, не так ли? Вы не имеете в виду только var completionStateIconName: String { task.isCompleted ? "circle.fill" : "circle" }?
Я продолжу это видео и последующие видео и посмотрю, станет ли использование Combine необходимым в будущем ...
Учитывая, что
Task- этоstruct,didSetподойдет. Но то же самое и с вычисляемым свойствомvar completionStateIconName: String { completionStateIconName ? task.isCompleted ? "circle.fill" : "circle" }. И просматривая оставшуюся часть видео, мы видим, что он даже не конвертируетTaskCellдля использованияcompletionStateIconName, так что нет никаких причин вводить это свойство вообще!