Swift 合并:将发布者变成只读的 CurrentValueSubject
Swift Combine: turn a publisher into a read-only CurrentValueSubject
有时我的视图模型使用 @Published
属性 或 PassthroughSubject
,但我不希望它对外界可写。很简单,把它变成 public AnyPublisher
并保持可写的私有,像这样:
class ViewModel {
@Published private var _models = ["hello", "world"]
var models: AnyPublisher<[String], Never> {
return $_models.eraseToAnyPublisher()
}
}
let viewModel = ViewModel()
viewModel.models.sink { print([=11=]) }
但是如果您还想读取值 "on demand" 怎么办?例如对于这种情况:
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 { [=13=] }).map { value, index in
value[index]
}.eraseToAnyPublisher()
}
}
let viewModel = ViewModel()
viewModel.models.sink { print([=13=]) }
viewModel.selectedModel.sink { print([=13=]) }
viewModel.selectedIndex = 1
但是添加selectedIndex
属性、selectedModel
发布者、设置selectedIndex和订阅发布者有点麻烦..都是因为我想成为能够读取 viewModel.models
的当前值(并且不可写)。
有更好的解决方案吗?
为什么不简单地保留 @Published
属性 public,同时将其 setter 设为私有?这应该完全符合您的要求,同时还能使您的代码保持最少和干净。
class ViewModel {
@Published public private(set) var models = ["hello", "world"]
}
let viewModel = ViewModel()
viewModel.$models.sink { print([=10=]) }
有时我的视图模型使用 @Published
属性 或 PassthroughSubject
,但我不希望它对外界可写。很简单,把它变成 public AnyPublisher
并保持可写的私有,像这样:
class ViewModel {
@Published private var _models = ["hello", "world"]
var models: AnyPublisher<[String], Never> {
return $_models.eraseToAnyPublisher()
}
}
let viewModel = ViewModel()
viewModel.models.sink { print([=11=]) }
但是如果您还想读取值 "on demand" 怎么办?例如对于这种情况:
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 { [=13=] }).map { value, index in
value[index]
}.eraseToAnyPublisher()
}
}
let viewModel = ViewModel()
viewModel.models.sink { print([=13=]) }
viewModel.selectedModel.sink { print([=13=]) }
viewModel.selectedIndex = 1
但是添加selectedIndex
属性、selectedModel
发布者、设置selectedIndex和订阅发布者有点麻烦..都是因为我想成为能够读取 viewModel.models
的当前值(并且不可写)。
有更好的解决方案吗?
为什么不简单地保留 @Published
属性 public,同时将其 setter 设为私有?这应该完全符合您的要求,同时还能使您的代码保持最少和干净。
class ViewModel {
@Published public private(set) var models = ["hello", "world"]
}
let viewModel = ViewModel()
viewModel.$models.sink { print([=10=]) }