Search code examples
reactive-cocoareactive-cocoa-3

Create a signal that emits one object then completes?


With RxSwift, I would do Observable.just(1) which will emit 1 then emit completed.

It looks like with RAC2 you could do: [RACSignal return:@1]

How do I do that with RAC3?

To be more clear... I'm looking for a way to create a RAC3 Signal that produces a single hard-coded value. How would I do that? (SignalProducer(value: 1) doesn't work that way.)


Solution

  • After reading the discussion, I think the answer by Charlotte Tortorella stands correct: you achieve the required behavior with SignalProducer(value: 1).

    I think the problem is a miss understanding about what Signal and SignalProducer are.

    As described here, a Signal in ReactiveSwift is a hot RACSignal in RAC 2.0 or Observable in Rx, and a SignalProducer in ReactiveSwift is a cold RACSignal in RAC 2.0 or Observable in Rx. This is deliberate deviation from other reactive frameworks as well as from RAC < 3.0.

    This means, you most likely have a method that takes a cold RACSignal or Observable since you want it to fire for every subscriber.

    So if you want to convert your RAC 2.0 code, that expects a cold Signal or Observable, you will need to change it to take a SignalProducer in RAC >= 3.0.

    As an illustration take this example in ObjC and RAC 2.0:

    -(void)observeSignal:(RACSignal *)signal
    {
        [signal subscribeNext:^(NSNumber *x) {
            NSLog(@"Next: %@", x);
        } completed:^{
            NSLog(@"Completed");
        }];
    }
    

    Calling this method like this

    RACSignal *signal = [RACSignal return:@(1)];
    [self observeSignal:signal];
    [self observeSignal:signal];
    

    (twice for illustration of the behaviour on each subscription) will print

    Next: 1
    Completed
    Next: 1
    Completed
    

    In Swift and with ReactiveCocoa 5.0, an equivalent implementation could look like this

    func observe(produer: SignalProducer<Int, NoError>) {
        produer.start { event in
            switch event {
            case .value(let value):
                print("Next: \(value)")
            case .completed:
                print("Completed")
            default:
                break
            }
        }
    }
    

    Called like this

    let producer = SignalProducer<Int, NoError>(value: 1)
    observe(produer: producer)
    observe(produer: producer)
    

    it produces the same output

    Next: 1
    Completed
    Next: 1
    Completed
    

    The swift version might look a bit bulkier, but if you only need the Next/value Events they look more or less the same. Note, that you'll need to start the producer instead of just observe the signal.

    So in conclusion:

    You are correct, theres no way to provide an equivalent if you expect a Signal. For an equivalent implementation, you will need to change your function to take a SignalProducer. SignalProducer is equivalent to a cold Signal which will fire for each subscriber.