Sometimes my viewmodel uses a @Published
property or a PassthroughSubject
, but I don't want this to be writeable to the outside world. Easy enough, turn it into a public AnyPublisher
and keep the writable one private, like this:
class ViewModel {
@Published private var _models = ["hello", "world"]
var models: AnyPublisher<[String], Never> {
return $_models.eraseToAnyPublisher()
}
}
let viewModel = ViewModel()
viewModel.models.sink { print($0) }
But what if you want to be able to read the value "on demand" as well? For example for this situation:
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(viewModel.models[indexPath.row])
}
}
Obviously, the above code doesn't work.
I thought about using a CurrentValueSubject
, but its value is writable too, plus I'm having a hard time turning a Publisher into a CurrentValueSubject anyway.
My current solution is to add something like this on the viewmodel:
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
But it's a bit of a chore to add the selectedIndex
property, the selectedModel
publisher, set the selectedIndex and subscribe to the publisher.. all because I want to be able to read the current value of viewModel.models
(and not have it writable).
Any better solutions?