Как связать производителей сигналов, передающих результаты

#swift #reactive-cocoa #reactive-cocoa-4

#swift #реактивный-cocoa #реактивный-cocoa-4

Вопрос:

Учитывая функции multiply() и convert() (минимальные примеры концепции, я на самом деле планирую запрашивать удаленный сервер внутри каждой функции), каков более короткий способ реализации multiplyAndConvert() ?

 // call two async functions passing the result the from the first 
// as the argument to the second
multiplyAndConvert(3).startWithResult { val in
    print("(val.value!)")
}

func convert(_ val: Int) -> SignalProducer<String, NSError> {
    return SignalProducer<String, NSError> { observer, _ in
        observer.send(value: String(val))
        observer.sendCompleted()
    }
}

func multiply(_ val: Int) -> SignalProducer<Int, NSError> {
    return SignalProducer<Int,NSError> { observer, _ in
        observer.send(value: val * 2)
        observer.sendCompleted()
    }
}

func multiplyAndConvert(_ val: Int) -> SignalProducer<String, NSError> {
    return SignalProducer<String, NSError> { observer, _ in
        multiply(val).startWithResult { res2 in
            switch res2 {
                case .success(let val2):
                    convert(val2).startWithResult { res3 in
                        switch res3 {
                            case .success(let val3):
                                observer.send(value:val3)
                                observer.sendCompleted()
                            case .failure(let err):
                                observer.send(error:err)
                        }
                    }
                case .failure(let err):
                    observer.send(error: err)
            }
            observer.sendCompleted()
        }
    }
}
  

Я знаю, что должен быть более элегантный способ сделать это, но я в тупике относительно того, что это такое. Я поиграл с map, flatMap, flatten и большинством других функций, но не могу найти лучшего решения.

Ответ №1:

Используйте flatMap:

 func multiplyAndConvert(_ val: Int) -> SignalProducer<String, NSError> {
  return multiply(val).flatMap(.concat) { multipliedVal in
    return convert(multipliedVal)
  }
}
  

вы также можете передать convert flatMap напрямую в качестве второго аргумента, а не закрывать его, поскольку это уже правильный тип ( Int -> SignalProducer<String, NSError> )

 multiply(val).flatMap(.concat, transform: convert)
  

но если convert функция ссылается self в вашем расширенном коде, это может привести к циклам сохранения, и в этом случае вам потребуется передать захват закрытия [weak self]

Объяснение

что flatMap здесь делает, так это принимает ваш сигнал умножения:

 -----.value(multipliedValue)-.completed
  

и отображает его в сигнал сигналов, используя предоставленное замыкание:

     .value(convertedMultipliedValue)
    /
----x-.completed
  

а затем «выравнивает» этот сигнал сигналов (используя .Стратегия сглаживания Concat, которая сглаживает сигнал сигналов путем объединения всех дочерних сигналов — но здесь это не имеет значения, потому что мы сглаживаем только один дочерний сигнал), чтобы дать нам:

 ----.value(convertedMultipliedValue)-.completed