Пользовательский оператор изменения с использованием Combine - PullRequest
1 голос
/ 14 июля 2020

Я хочу создать объединение издателя / подписчика / подписки, которое ведет себя следующим образом:

struct Change<Value> {
  let new: Value
  let previous: Value?
}

let pub = PassthroughSubject<Int, Never>()

let cancellable = pub
  .change()
  .sink { (change: Change<Int>) -> Void in
    print(change)
  }

pub.send(1) // prints Change(new: 1, previous: nil)
pub.send(2) // prints Change(new: 2, previous: 1)
pub.send(3) // prints Change(new: 3, previous: 2)

Возникли проблемы с правильной реализацией. Я создал свой собственный Издатель / Подписку для обертывания внешних вызовов API и сортировки, но не могу найти правильную комбинацию, когда необходимо сохранить какое-то состояние, как предыдущее значение в этом примере (я думаю, это означает, что вам нужно пользовательский подписчик?)

Альтернативный синтаксис с той же семантикой также будет приемлем, если по какой-то причине синтаксис .change() не работает.

1 Ответ

3 голосов
/ 14 июля 2020

Вы можете построить оператор change из операторов scan и map следующим образом:

struct Change<Value> {
    var old: Value?
    var new: Value
}

extension Publisher {
    func change() -> Publishers.Map<Publishers.Scan<Self, (Optional<Self.Output>, Optional<Self.Output>)>, Change<Self.Output>> {
        return self
            .scan((Output?.none, Output?.none)) { (state, new) in
                (state.1, .some(new))
            }
            .map { (old, new) in
                Change(old: old, new: new!)
            }
    }
}

Демо:

let pub = PassthroughSubject<Int, Never>()
let ticket = pub
    .change()
    .sink { print($0) }

pub.send(1)
// Output: Change<Int>(old: nil, new: 1)

pub.send(2)
// Output: Change<Int>(old: Optional(1), new: 2)

pub.send(3)
// Output: Change<Int>(old: Optional(2), new: 3)
...