iosobjective-creactive-cocoaracsignal

how to wait until the task is completed and then return a signal, reactive cocoa


-(RACSignal*)finalPackage {

RACSignal *endPoint = [[DGConfiguration sharedInstance].apiConfiguration          
urlTemplate:DGAPIUrlLocalWatchList];` // 1.

return [[endPointRequestSignal map:^id(NSString *endPoint) { // 2.
    return service([NSURL URLWithString: endPoint]); 
}].flatten map:^id(NSArray *episodes) { // 3.
    NSMutableArray *info= [NSMutableArray array];
    __block NSArray *result=@[@(9)]; // test value is 9, result will be updated during callback block

    [episodes enumerateObjectsUsingBlock:^(NSDictionary *item, NSUInteger idx, BOOL *stop) {
        [info addObject:@{@"id":item[@"id"],@"links":item[@"links"]}];
    }];

    [[DGManager sharedInstance] updateVideoStateWith:info callback:^(NSArray *response) { // 4.
        dispatch_async(dispatch_get_main_queue(), ^{
            NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
            result  =   [[response sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] copy];

        });
    }];

    return [RACSignal return:result]; // 5.
}].flatten;
}

Lets me explain what I am trying to do.

  1. I wrap the endPoint url via endPoint signal
  2. Using map to extract url and do a service call (service([NSURL URLWithString: endPoint]))
  3. Using map to extract info from step 2 and create info data
  4. Do updateVideoStateWith with a callback
  5. Return a signal which contains result

Eventually, when I subcribe to finalPackage signal, the return is initialized value which is 9.I realize that the updateVideoStateWith call back will take time to return the result.

My question is how can I force return [RACSignal return:result] wait until the data is updated from callback block. I did tried takeUntilBlock but not sure how to use it. I also think about using switchToLatest but still no luck.


Solution

  • Cross posting my answer from the GitHub issue:

    - (RACSignal*)finalPackage {
        return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
                RACSignal *endPointSignal = [[DGConfiguration sharedInstance].apiConfiguration urlTemplate:DGAPIUrlLocalWatchList];
                [[endPointSignal map:^id(NSString *endPoint) { 
                    // map your endpoints to episodes and return the array of episodes
                }] subscribeNext:^(NSArray* episodes) {
                    // Create your initial result array
                    [[DGManager sharedInstance] updateVideoStateWith:info callback:^(NSArray *response) { 
                        // Do whatever work you need to do with the response to modify the result array
                        [subscriber sendNext:result];
                        [subscriber sendComplete];                     
                    }];
                } error:^(NSError* error) {
                    [subscriber sendError:error];
                ]];
                return nil;
            }];
    }
    

    Note: if you're returning a RACSignal* when mapping from the endPoint NSString you'll want to flattenMap instead of map, flattenMap will flatten out the signal that is returned into the value it emits.