Ok, so now that I know how to create a looping retry I'm stuck on another problem. I think I know the why, but I can't seem to figure out the how. In the code below I have a sequence of events that will collect a location. Reverse geocode it and finally compare it to a list of allowed country codes. My current problem is that the alert asking the user to allow location services for the app will display even if no error have been sent. I think this is because the call to [self retryWithAlert] will be called straight away when the sequence is set up and I shouldn't place the code for creating and showing the alert where it currently is. But when I try to wrap this in a [RACSignal create:] I just can't seem to get it right. And without further ado, here is the code:
First the sequence itself
_locationManager = [[CLLocationManager alloc] init];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyKilometer];
@weakify( self )
[[[[[_locationManager
rac_fetchLocationSignal]
catchTo:[self retryWithAlert]]
flattenMap:^RACStream *( CLLocation *newLocation ) {
@strongify( self )
return [self reverseGeocodeLocation:newLocation];
}]
flattenMap:^RACStream *( NSString *ISOCountryCode ) {
@strongify( self )
return [self checkCountryPermissibleSignal:ISOCountryCode];
}]
subscribeNext:^( id x ) {
[_locationManager stopUpdatingLocation];
NSLog( @"Country is valid!" );
} error:^( NSError *error ) {
NSLog( @"Error: %@", error );
}];
[_locationManager startUpdatingLocation];
And here is the code for [self retryWithAlert]
- (RACSignal *)retryWithAlert
{
UIAlertView *retryAlert =
[[UIAlertView alloc] initWithTitle:@"Trouble"
message:@"Make sure location services are enabled and that the app is allowed to use them."
delegate:self
cancelButtonTitle:@"Retry"
otherButtonTitles:nil];
[retryAlert show];
return [[[retryAlert
rac_buttonClickedSignal]
flattenMap:^RACStream *( id value ) {
[_locationManager startUpdatingLocation];
return [_locationManager rac_fetchLocationSignal];
}]
catch:^RACSignal *( NSError *error ) {
return [self retryWithAlert];
}];
}
You're really close. As you noted, the problem is that the -retryWithAlert
message is sent immediately when this code runs. This happens because that message send is part of the code that constructs the signal chain in the first place. You only want that message to be sent when the catchTo
signal is triggered.
What I think you want, instead of sending -retryWithAlert
immediately, is to defer that until the catchTo
'd signal is subscribed to (at which point it's okay to display the alert). This is most easily accomplished with -[RACSignal defer:]
, which lets you put your -retryWithAlert
message send in a block that isn't called until a subscription occurs.
@weakify( self )
[[[[[[_locationManager
rac_fetchLocationSignal]
catchTo:[RACSignal defer:^{ // <--- defer until subscription
@strongify( self );
return [self retryWithAlert];
}]]
flattenMap:^RACStream *( CLLocation *newLocation ) {
@strongify( self )
return [self reverseGeocodeLocation:newLocation];
}]
... (etc)