【问题标题】:Swift Combine: turn a publisher into a read-only CurrentValueSubjectSwift Combine:将发布者变成只读的 CurrentValueSubject
【发布时间】:2020-04-30 10:04:23
【问题描述】:

有时我的视图模型使用@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,但它的值也是可写的,而且无论如何我都很难将 Publisher 变成 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 的当前值(并且不可写)。

有更好的解决方案吗?

【问题讨论】:

    标签: swift combine


    【解决方案1】:

    您为什么不简单地将@Published 属性公开,同时将其设置器设为私有?这应该完美地满足您的要求,同时保持您的代码最小和干净。

    class ViewModel {
      @Published public private(set) var models = ["hello", "world"]
    }
    
    let viewModel = ViewModel()
    viewModel.$models.sink { print($0) }
    

    【讨论】:

    • ...我从来没有意识到我能做到这一点?‍♂️
    • @KevinRenskers 我猜是因为 Swift 会自动合成 getter 和 setter,除非您看到可以向它们添加不同的可访问性修饰符,否则您甚至不会认为这是可能的。
    • 这里的问题是你不能在protocol中定义你的var如果它是@Published,所以你不能实现抽象,除非我误解了。跨度>
    • @bkbeachlabs 您无法将属性包装器添加到协议定义中,但您仍然可以使用一些额外代码在协议中定义 @Published 属性要求。请参阅this answer 了解如何执行此操作。顺便说一句,协议并不是实现抽象的唯一方法。
    猜你喜欢
    • 1970-01-01
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    相关资源
    最近更新 更多