Question

I'm currently taking my first steps in ReactiveCocoa and I experience some steep learning curve to understand the principals.

Anyway here's what I already came up with.

I bind a NSArray property to a RACSignal to be able to react to the incoming JSON data over network.

- (void)updateRandomUserData 
{
    @weakify(self);
    RAC(self, users) = [[self fetchRandomUserData] doNext:^(NSDictionary *json){
    @strongify(self);
    NSMutableArray *randomUsers = [NSMutableArray array];
        for (NSDictionary *dict in json[@"data"]) {
           BKRandomUser *randomUser = [MTLJSONAdapter modelOfClass:[BKRandomUser class] fromJSONDictionary:dict error:nil];
           [randomUsers addObject:randomUser];
        }

        self.users = randomUsers;
    }];
}

The signal creation looks like this:

- (RACSignal *)fetchRandomUserData
{

    return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

      NSURLRequest *request = [NSURLRequest requestWithURL:url];

      AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
      [subscriber sendNext:JSON];
      } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON, NSError *error) {
        [subscriber sendError:error];
      }];

      [operation start];

      return [RACDisposable disposableWithBlock:^{
          [operation cancel];
      }];

      }] doError:^(NSError *error) {
        NSLog(@"error: %@", [error description]);
    }];

}

Now when the web service doesn't provide any data I want to react to this. Right now the app crashes with the following statement, which I honestly don't understand:

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Received error from name: [[+createSignal:] -doError:] -doNext: in binding for key path "users" on : (null)'

What am I missing here?

Thank you!

Was it helpful?

Solution

The accepted answer will work around the problem, but there's a more idiomatic, elegant way to handle this:

First off, instead of using -doNext:, use -map: to transform your JSON into the array of users:

RAC(self, users) = [[self fetchRandomUserData] map:^(NSDictionary *json){
    NSMutableArray *randomUsers = [NSMutableArray array];
    for (NSDictionary *dict in json[@"data"]) {
       BKRandomUser *randomUser = [MTLJSONAdapter modelOfClass:[BKRandomUser class] fromJSONDictionary:dict error:nil];
       [randomUsers addObject:randomUser];
    }

    return randomUsers;
}];

Then, to handle the error, you can use -catch::

RAC(self, users) = [[[self fetchRandomUserData] map:^(NSDictionary *json){
    NSMutableArray *randomUsers = [NSMutableArray array];
    for (NSDictionary *dict in json[@"data"]) {
       BKRandomUser *randomUser = [MTLJSONAdapter modelOfClass:[BKRandomUser class] fromJSONDictionary:dict error:nil];
       [randomUsers addObject:randomUser];
    }

    return randomUsers;
}] catch:^(NSError *error) {
    return [RACSignal return:@[]];
}];

In this example, if an error happens we catch it and replace it with an empty array. You could do whatever you wanted there. Replace it with nil, or +[RACSignal empty] if you just want to ignore the whole thing. Or call another method that returns a RACSignal *.

OTHER TIPS

  1. By writing RAC(self, users), you means you want to binding the signal's return to the property users, but you are not returning anything in the signal handler.
  2. You are using the doNext, which is a injection for current signal. You may want to subscribe it instead of injecting the side-effect.

Solve:

You can remove the RAC(self, users) binding if you want to assign it in the subscribeNext side-effect, so just subscribe for the signal would be OK for your case, like this:

@weakify(self);
[[self fetchRandomUserData] subscribeNext:^(NSDictionary *json) {
    @strongify(self);
    NSMutableArray *randomUsers = [NSMutableArray array];
    for (NSDictionary *dict in json[@"data"]) {
        BKRandomUser *randomUser = [MTLJSONAdapter modelOfClass:[BKRandomUser class] fromJSONDictionary:dict error:nil];
        [randomUsers addObject:randomUser];
    }

    self.users = randomUsers;
}];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top