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)
}
}
