I am very new to RxSwift and trying to do the following:
My application needs to enable selection of "Elements" where the selection mode can be single
selection (a new selection replaces an old selection) or multiple
, where a selection is added to any old selection.
In single
mode, if the new selection is the old selection, then my selection results must become empty (toggles the selection by selecting the same element).
In multiple
mode, if the new selection is part of the old selection, the newly selected element is removed from the current selection.
I have three existing subjects: selectionModeSubject
is a BehaviorSubject
containing the single
or multiple
enumeration. selectSubject
represents the new selection requested by the user, it is a PublishSubject
. Finally, currentSelectionSubject
, a BehaviorSubject
containing the current Set of elements that are selected.
I'm trying to have currentSelectionSubject
containing the resulting selection after selectSubject
fires.
Here is what I have:
Observable
.combineLatest(selectionModeSubject, selectSubject, currentSelectionSubject) { (mode, newSelection, currentSelection) -> Set<Element> in
switch mode {
case .single:
if currentSelection.contains(newSelection) {
return Set([newSelection])
} else {
return Set<Element>()
}
case .multiple:
if currentSelection.contains(newSelection) {
return currentSelection.filter({ (element) -> Bool in
return element != newSelection
})
} else {
return currentSelection.union(Set([newSelection]))
}
}
}
.bind(to: currentSelectionSubject)
.disposed(by: disposeBag)
The problem, amplified by my newbie RxSwift status, is that this observation code potentially fires whenever the selectionModeSubject
or currentSelectionSubject
fire. I would only want this to fire if selectSubject
is changed.
I have tried to insert a .distinctUntilChanged()
on the selectSubject
but I cannot seem to come to grasp with it.
Any tips would be appreciated.
withLatestFrom
is the way to go.
selectSubject.withLatestFrom(
Observable
.combineLatest(selectionModeSubject, currentSelectionSubject)) { newSelection, pair in
let (mode, currentSelection) = pair
return (mode, newSelection, currentSelection)
}.map { (mode, newSelection, currentSelection) -> Set<Element> in
switch mode {
case .single:
if currentSelection.contains(newSelection) {
return Set([newSelection])
} else {
return Set<Element>()
}
case .multiple:
if currentSelection.contains(newSelection) {
return currentSelection.filter({ (element) -> Bool in
return element != newSelection
})
} else {
return currentSelection.union(Set([newSelection]))
}
}
}
.bind(to: currentSelectionSubject)
.disposed(by: disposeBag)