Search code examples
iosswiftreactive-cocoa

How to merge Array of Signals into a single Signal ReactiveCocoa 4


In my setup, I have a GridView that consists of many GridViewCells. Each cell has a UITapGestureRecognizer.

For convenience, the GridView has the following function func cellsTapGestureRecognizers() -> [UITapGestureRecognizer?] which returns the UITapGestureRecognizers of all its cells (if they exist).

In the UIViewController, that uses the GridView, my goal is to treat the taps as Signals. I managed to do this but with the limitation that I create one Signal for each GridViewCell which seems unnecessary.

func createTapSignals() -> [Signal<Position, NoError>?] {
    var signals: [Signal<Position, NoError>] = []
    for maybeTap in self.gridView.cellsTapGestureRecognizers() {
        if let tap = maybeTap {
            let signal = tap.gestureSignalView()
                .map { $0 as! GridViewCell }
                .map {$0.position}
            signals.append(signal)
        }
    }
    return signals
}

Is it possible to merge all the signals that are created in createTapSignals() such that in the end there is only one signal left of type Signal<Position, NoError>?

I looked into the documentation, but only found the way how to merge SignalProducers by using flatten(.Merge). Is there a similar approach I can take to merge an Array of Signals.

Note that gestureSignalView() which is used in the function above returns a Signal<UIView, NoError> that carries the UIView of the UITapGestureRecognizer (inspired by @NachoSoto's gist).


Solution

  • I found the solution myself after searching around in the RAC Github repo.

    The (static) function merge was exactly was I was looking for. It's part of the SignalType protocol (which Signal conforms to) and is implemented in a protocol extension.

    So, I can just do the following:

    let signals = createTapSignals()
    let mergedSignal = Signal.merge(signals)
    

    For reference, here is how merge is implemented:

    public static func merge<S: SequenceType where S.Generator.Element == Signal<Value, Error>>(signals: S) -> Signal<Value, Error> {
        let producer = SignalProducer<Signal<Value, Error>, Error>(values: signals)
        var result: Signal<Value, Error>!
    
        producer.startWithSignal { (signal, _) in
            result = signal.flatten(.Merge)
        }
    
        return result
    }