Hans Sjunnesson
Hans Sjunnesson

Reputation: 22289

ReactiveCocoa takeUntil and takeWhile doesn't send last "next"

Consider the following snippet:

- (RACSignal *)startRouting {
...
}

- (RACSignal *)updateRoutingWithSession:(NSString *)session {
...
}

- (RACSignal *)fetchFlights {
    return [[self startRouting] flattenMap:^RACStream *(NSString *session) {
        return [[[[self updateRoutingWithSession:session]
                        delay:2.0f]
                        repeat]
                        takeUntilBlock:^BOOL(RACTuple *operationAndResponse) {
                            AFHTTPRequestOperation *operation = [operationAndResponse first];
                            NSDictionary *response = [operationAndResponse second];
                            return [operation isCancelled] || 100 == [response[kPercentComplete] intValue];
                        }];
    }];
}

What's happening here is that startRouting returns a RACSignal which sends a session ID. updateRoutingWithSession: returns a RACSignal which sends an NSDictionary looking including a PercentComplete attribute. There's a two second delay between polls.

fetchFlights will run until updateRoutingWithSession: has a PercentComplete of 100.

My issue here is that the very last sendNext:, where the takeUntilBlock returns true, doesn't reach the RACSubscriber.

What am I missing?

Upvotes: 3

Views: 2687

Answers (2)

Hans Sjunnesson
Hans Sjunnesson

Reputation: 22289

I found this in the world of RX. This is commonly solved by merging two signals. One that takes the repeated source until the the predicate is true. And the other that skips while the predicate is true.

This looks something like this

 BOOL (^finished)(id _) = ^BOOL(id _) {
    return predicate; // BOOLean here
}

// You want a multicast signal, because multiple signals will subscribe to the source.
// Multicasting it means that you won't get repeated api-requests, in this case.
RACMulticastConnection *source = [[theSignal repeat] publish];

RACSignal *whileNotDone = [source.signal takeUntilBlock:finished];
RACSignal *whenDone = [[source.signal skipUntilBlock:finished] take:1];
RACSignal *merged = [RACSignal merge:@[whileNotDone, whenDone]];

[source connect]; // Needed for a multicast signal to initiate.

The merged signal will sendNext every next in source including the very last one. Then sendCompleted.

Some references from the world of RX:

Upvotes: 2

allprog
allprog

Reputation: 16780

To clarify: your problem is that the next that triggers the completion is not sent out? takeUntilBlock will propagate the nexts until the predicate is NO. (documentation) Therefore, the last next will not be sent. But you can subscribe to completion which should happen in this case.

Upvotes: 1

Related Questions