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!
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 *
.