Это пример реализации для описанного поведения.
Помните, что stackoverflow не предназначен для такой работы. Вы должны показать код, который вы написали, чтобы попытаться решить вашу проблему в первую очередь.
// This encodes a database action. You can subscribe to view model's `dbAction` to perform your desired side effect.
enum DBAction<T> {
case insert(object: T)
case delete(object: T)
}
class SomeViewModel<Object> {
struct Output {
let currentObject: Observable<Object>
let remainingObjectCount: Observable<Int>
let dbAction: Observable<DBAction<Object>>
}
struct Input {
let acceptOrDecline: Observable<(keepOrDrop: Bool, applyToAll: Bool)>
}
let totalObjectCount: Int
let objects: Observable<Object>
init(objects: [Object]) {
self.totalObjectCount = objects.count
self.objects = .from(objects) // 1
}
func transform(input: Input) -> Output {
let applyToAll: Observable<Void> = input.acceptOrDecline.map { $0.applyToAll }.filter { $0 == true }.map { _ in }
let acceptOrDecline = input.acceptOrDecline.map { $0.keepOrDrop }
let currentObject = Observable.zip( // 2
objects,
acceptOrDecline.map { _ in }.startWith() // 3
) { object, _ in object }
.takeUntil(applyToAll) // 4
.share()
// 5
let actionForCurrent = input.acceptOrDecline.flatMap { tuple in
tuple.applyToAll ? Observable.repeatElement(tuple.keepOrDrop, scheduler: MainScheduler.instance) : .just(tuple.keepOrDrop)
}
let dbAction = Observable.zip(
objects,
actionForCurrent
) { (object: Object, shouldKeep: Bool) -> DBAction<Object> in
if shouldKeep {
return DBAction.insert(object: object)
} else {
return DBAction.delete(object: object)
}
}
let remainingObjectCount = currentObject.scan(totalObjectCount) { acc, _ in
acc - 1
}.concat(.just(0))
return Output(
currentObject: currentObject,
remainingObjectCount: remainingObjectCount,
dbAction: dbAction
)
}
}
- Здесь я создаю obversable, который будет испускать каждый элемент в исходном массиве, один за другим.
- zip объединяет элементы из двух наблюдаемых. Хорошая вещь с zip состоит в том, что он будет ожидать отдельного элемента от каждого источника. При подписке на результат zip, новый элемент будет выпущен после
input.acceptOrDecline
. Следовательно, мы будем получать новый объект после каждого решения.
startWith()
запустит первый выброс, так что мы получим первый объект, по которому пользователь должен принять решение.
takeUntil
сделает нашу наблюдаемую завершенной, когда applyToAll
испустит. Так что мы не получаем новый элемент, когда установлен флажок applyToAll
.
repeatElement
будет бесконечно повторять элемент. Поэтому, когда applyToAll
истинно, мы будем повторять решение до бесконечности. Поскольку мы сжали результат от flatMap
до objects
, он повторит решение о количестве оставшихся объектов в objects
.
Чтобы построить источник, наблюдаемый для модели представления, предполагая, что вы используете два UIButton
s и UISwitch
let acceptButton: UIButton
let dropButton: UIButton
let applyToAll: UISwitch
let accept = acceptButton.rx.tap.map { true }
let drop = dropButton.rx.tap.map { false }
let input = Input(
acceptOrDecline: Observable.combineLatest(
Observable.merge(accept, drop),
applyToAll.rx.value
) { (keepOrDrop: $0, applyToAll: $1) }
)
Обратите внимание , что это предлагаемая реализация, которая компилируется, но я не тестировал. Здесь вы найдете руководства для реализации желаемого поведения, но я не могу гарантировать, что это на 100% правильно.