Swift Combine:将发布者转变为只读CurrentValueSubject

时间:2020-04-30 10:04:23

标签: swift combine

有时,我的视图模型使用了@Published属性或PassthroughSubject,但是我不希望它对外部世界是可写的。足够简单,将其变成公共AnyPublisher并保持可写状态为私有,如下所示:

class ViewModel {
  @Published private var _models = ["hello", "world"]

  var models: AnyPublisher<[String], Never> {
    return $_models.eraseToAnyPublisher()
  }
}

let viewModel = ViewModel()
viewModel.models.sink { print($0) }

但是,如果您还希望能够读取“按需”值,该怎么办?例如,这种情况:

extension ViewController: UICollectionViewDelegate {
  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    print(viewModel.models[indexPath.row])
  }
}

很显然,以上代码不起作用。

我曾考虑过使用CurrentValueSubject,但是它的值也是可写的,而且无论如何我都很难将发布者变成CurrentValueSubject。

我当前的解决方案是在视图模型上添加以下内容:

class ViewModel {
  @Published private var _models = ["hello", "world"]
  @Published var selectedIndex: Int?

  var models: AnyPublisher<[String], Never> {
    return $_models.eraseToAnyPublisher()
  }

  var selectedModel: AnyPublisher<String, Never> {
    return models.combineLatest($selectedIndex.compactMap { $0 }).map { value, index in
      value[index]
    }.eraseToAnyPublisher()
  }
}

let viewModel = ViewModel()
viewModel.models.sink { print($0) }

viewModel.selectedModel.sink { print($0) }
viewModel.selectedIndex = 1

但是添加selectedIndex属性,selectedModel发布者,设置selectedIndex并订阅发布者有点麻烦。所有这些都是因为我希望能够读取当前值的viewModel.models(并且不可写)。

有更好的解决方案吗?

1 个答案:

答案 0 :(得分:1)

为什么不将@Published属性设为公开,而将其设置程序设为私有?这应该完全满足您的要求,同时还可以使代码最少且干净。

class ViewModel {
  @Published public private(set) var models = ["hello", "world"]
}

let viewModel = ViewModel()
viewModel.$models.sink { print($0) }