I have a following structure:
class FileUploadCellViewModel {
@Published var isUploaded: Bool = false
}
class FileUploadScreenViewModel: ObservableObject {
@Published var viewModels: [FileUploadCellViewModel] = []
@Published var isSendButtonEnabled: Bool = false
private var cancellables: Set<AnyCancellable> = []
init() {
let publisher = $viewModels
// ?? return a single publisher with `true` if all `isUploaded` are `true`
}
let isDateCorrectPublisher = ...
publisher
.combineLatest(isDateCorrectPublisher)
.sink {
isSendButtonEnabled = $0 && $1
}
.store(in: &cancellables)
}
}
let screen = FileUploadScreenViewModel()
let viewModel1 = FileUploadCellViewModel()
let viewModel2 = FileUploadCellViewModel()
screen.viewModels.append(viewModel1) // expected: isSendButtonEnabled: false
screen.viewModels.append(viewModel2) // expected: isSendButtonEnabled: false
viewModel1.isUploaded = false // expected: isSendButtonEnabled: false
viewModel2.isUploaded = true // expected: isSendButtonEnabled: false
viewModel1.isUploaded = true // expected: isSendButtonEnabled: true
How do I observe each insertion into the viewModels
array and after an element is inserted, observe its isUploaded
property?
I have found a convenience tool to collect an array of publishers, maybe it would be helpful here.
CodePudding user response:
You are on the right track with needing a CombineLatestCollection
for combining all isUploaded
values of all cell view models.
My answer uses the combineLatest
operator from the article you've linked.
Once you've combined all publishers, you simply need to call allSatisfy
on the [Bool]
to see if all cell view models finished uploading.
Now the only part missing is updating the subscription whenever the viewModels
array changes - you can do this by observing the $viewModel
publisher and passing in the updated array to a method, which combines the isUploaded
properties of the updated view models.
class FileUploadScreenViewModel {
@Published var viewModels: [FileUploadCellViewModel] = []
@Published var isSendButtonEnabled: Bool = false
private var subscriptions = Set<AnyCancellable>()
init() {
// Whenever the viewModels array changes, set up the subscription on each element of the array
$viewModels.sink { [weak self] viewModels in
self?.bindIsSendButtonEnabled(viewModels: viewModels)
}.store(in: &subscriptions)
}
// Combine all isUploaded values from each element of viewModels and update isSendButtonEnabled accordingly
private func bindIsSendButtonEnabled(viewModels: [FileUploadCellViewModel]) {
let areUploaded = viewModels
.map(\.$isUploaded)
.combineLatest
.map { areUploaded in
areUploaded.allSatisfy { isUploaded in
isUploaded == true
}
}
let isDateCorrectPublisher = Just(true)
areUploaded
.combineLatest(isDateCorrectPublisher)
.map { $0 && $1 }
.assign(to: &$isSendButtonEnabled)
}
}