SwiftUI 和组合

SwiftUI and Combine

我正在关注 video on the Firebase YouTube channel。从 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 是什么东西? (它在 init{} 之前被定义为 private var cancellables = Set<AnyCancellable>()。)第三,为什么所有这些代码,而不是简单的:

task.completionStateIconName = task.isCompleted ? "checkmark.circle.fill" : "circle"

这似乎给出了相同的结果,但是否会出现第一个代码片段有效而第二个代码片段无效的情况?

$task(带有 $ 前缀)是 @Published 属性 包装器的投影值,它 returns 类型的变量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)

现在,上面的 returns 是 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"
   }
}